@@ -19,21 +19,34 @@ const ENV_PREFIX_SEPARATOR: &str = "_";
1919/// Example: `APP_DATABASE__URL` sets the `database.url` field.
2020const ENV_SEPARATOR : & str = "__" ;
2121
22+ /// Separator for list elements in environment variables.
23+ ///
24+ /// Example: `APP_API_KEYS=abc,def` sets the `api_keys` array field.
25+ const LIST_SEPARATOR : & str = "," ;
26+
27+ /// Trait defining the list of keys that should be parsed as lists in a given [`Config`]
28+ /// implementation.
29+ pub trait Config {
30+ /// Slice containing all the keys that should be parsed as lists when loading the configuration.
31+ const LIST_PARSE_KEYS : & ' static [ & ' static str ] ;
32+ }
33+
2234/// Loads hierarchical configuration from YAML files and environment variables.
2335///
2436/// Loads configuration in this order:
2537/// 1. Base configuration from `configuration/base.yaml`
2638/// 2. Environment-specific file from `configuration/{environment}.yaml`
2739/// 3. Environment variable overrides prefixed with `APP`
2840///
29- /// Nested keys use double underscores: `APP_DATABASE__URL` → `database.url`
41+ /// Nested keys use double underscores: `APP_DATABASE__URL` → `database.url` and lists are separated
42+ /// by `,`.
3043///
3144/// # Panics
3245/// Panics if current directory cannot be determined or if `APP_ENVIRONMENT`
3346/// cannot be parsed.
3447pub fn load_config < T > ( ) -> Result < T , config:: ConfigError >
3548where
36- T : DeserializeOwned ,
49+ T : Config + DeserializeOwned ,
3750{
3851 let base_path = std:: env:: current_dir ( ) . expect ( "Failed to determine the current directory" ) ;
3952 let configuration_directory = base_path. join ( CONFIGURATION_DIR ) ;
@@ -43,21 +56,33 @@ where
4356 let environment = Environment :: load ( ) . expect ( "Failed to parse APP_ENVIRONMENT." ) ;
4457
4558 let environment_filename = format ! ( "{environment}.yaml" ) ;
59+
60+ // We build the environment configuration source.
61+ let mut environment_source = config:: Environment :: with_prefix ( ENV_PREFIX )
62+ . prefix_separator ( ENV_PREFIX_SEPARATOR )
63+ . separator ( ENV_SEPARATOR )
64+ . try_parsing ( true )
65+ . list_separator ( LIST_SEPARATOR ) ;
66+
67+ // For all the list parse keys, we add them to the environment source. These are used to define
68+ // which keys should be parsed as lists.
69+ for key in <T as Config >:: LIST_PARSE_KEYS {
70+ environment_source = environment_source. with_list_parse_key ( key) ;
71+ }
72+
4673 let settings = config:: Config :: builder ( )
74+ // Add in settings from the base configuration file.
4775 . add_source ( config:: File :: from (
4876 configuration_directory. join ( BASE_CONFIG_FILE ) ,
4977 ) )
78+ // Add in settings from the environment-specific file.
5079 . add_source ( config:: File :: from (
5180 configuration_directory. join ( environment_filename) ,
5281 ) )
5382 // Add in settings from environment variables (with a prefix of APP and '__' as separator)
5483 // E.g. `APP_DESTINATION__BIG_QUERY__PROJECT_ID=my-project-id` sets
5584 // `Settings { destination: BigQuery { project_id } }` to `my-project-id`.
56- . add_source (
57- config:: Environment :: with_prefix ( ENV_PREFIX )
58- . prefix_separator ( ENV_PREFIX_SEPARATOR )
59- . separator ( ENV_SEPARATOR ) ,
60- )
85+ . add_source ( environment_source)
6186 . build ( ) ?;
6287
6388 settings. try_deserialize :: < T > ( )
0 commit comments