metre 0.2.6

Metre, the configuration loader for Rust
Documentation
//! use metre::Config;
//! use metre::ConfigLoader;
//!
//! #[derive(Config)]
//! #[config(rename_all = "snake_case")]
//! struct MyConfig {
//!   
//!   // a simple property
//!   port: u16,
//!   
//!   // a property with a default value
//!   #[config(default = std::net::SocketAddr::from(([0,0,0,0], 3000)))]
//!   addr: std::net::SocketAddr,
//!
//!   // some type that doesn't implement FromStr and with a custom merge function
//!   #[config(parse_env = parse_vec, merge = merge_vec)]
//!   custom_parse_env: Vec<String>,
//!
//!   // an optional value
//!   // you should only use Option for configurations that are really optional
//!   // under the hood metre creates a Config::Partial type that is a deep-partial version
//!   // of this struct, so it can be deserialized from partial configurations
//!   optional: Option<String>,   
//!   
//!   // a nested configuration
//!   // the nested type must also implement Config
//!   #[config(nested)]
//!   nested: NestedConfig,
//!
//!   // rename env variable
//!   // the default name for env variables is "{}{name}"
//!   // where name is the SCREAMING_SNAKE_CASE version of the
//!   // field name after applying rename and rename_all configurations
//!   // and the {} placeholder is filled with the auto calculated prefix
//!   #[config(env = "{}OTHER_NAME")]
//!   name: String,
//!
//!   // fixed env key (ignores prefixes)
//!   // this will ignore auto calculated prefixes and prefixes set by env_with_prefix loader calls
//!   #[config(env = "FIXED_ENV_KEY")]
//!   fixed_env_key: u64,
//!
//!   // skip env parsing for this variable
//!   #[config(skip_env)]
//!   skip_env: String
//! }
//!
//! #[derive(Config)]
//! // change the env prefix for this struct
//! #[config(env_prefix = "{}OTHER_")]
//! struct NestedConfig {
//!  #[config(rename = "other_deserialize_and_env_name")]
//!  deep_prop: String
//! }
//!
//! fn load_config() -> Result<MyConfig, metre::Error> {
//!   use metre::Format;
//!   
//!   // create an empty configuration object
//!   let mut loader = ConfigLoader::<MyConfig>::new();
//!
//!   // partial configurations can be added in stages to form the final configuration
//!   // each new stage will override the previous one for the present keys
//!   // you can control how the merge is done with the `#[config(merge = function_name)]` attribute
//!
//!   // add deep-partial defaults calculated from the `#[config(default = value)]` attributes
//!   loader.defaults()?;
//!   
//!   // add deep-partial values from config file
//!   #[cfg(feature = "json")]
//!   loader.file("./config.json", Format::Json)?;
//!
//!   // the same as above but will do nothing if the file doesn't exist
//!   // Jsonc format is json with comments
//!   #[cfg(feature = "jsonc")]
//!   loader.file_optional("./config.jsonc", Format::Jsonc)?;
//!   
//!   // from memory
//!   loader.code("port=3000", Format::Toml)?;
//!
//!   // form a url
//!   #[cfg(feature = "url")]
//!   loader.url("https://example.com/config.yaml", Format::Yaml)?;
//!
//!   // from a url but async
//!   #[cfg(feature = "url-async")]
//!   async {
//!     loader.url_async("https://example.com/config.json", Format::Json).await.expect("error loading config from url");
//!   };
//!
//!   // from env variables
//!   #[cfg(feature = "env")]
//!   loader.env()?;
//!
//!   // from env variables with a prefix
//!   #[cfg(feature = "env")]
//!   loader.env_with_prefix("MY_APP_")?;
//!
//!   // from env variables with a custom provider
//!   // env provider must implement the metre::EnvProvider trait
//!   // that is already implemented for several types of Maps
//!   
//!   #[cfg(feature = "env")]
//!   {
//!     let mut env_provider = std::collections::HashMap::from([( "MY_APP_PORT", "3000" )]);
//!     loader.env_with_provider_and_prefix(&env_provider, "MY_APP_")?;
//!   }
//!   // from a pre generated partial configuration
//!   // PartialMyConfig type is auto generated from the `#[derive(Config)]` macro
//!   // and equals to <MyConfig as Config>::Partial: PartialConfig
//!   // see the PartialConfig trait too see methods asociated with partial config structs
//!   let partial = PartialMyConfig { port: Some(3000), ..Default::default()  };
//!   loader.partial(partial)?;
//!
//!   // compute the final values from the sum of partial configurations
//!   // if after all the stages, required properties are still missing
//!   // a pretty error indicating the missing bits will be returned
//!   let config = loader.finish()?;
//!
//!   // here config has the type `MyConfig`
//!   assert_eq!(config.port, 3000);
//!
//!   Ok(config)
//! }
//!
//!
//! // this is only needed to parse env for types that does not implement FromStr
//! // you can return any error here that implements Display
//! fn parse_vec(value: &str) -> Result<Option<Vec<String>>, std::convert::Infallible> {
//!   let vec = value.split(",").map(String::from).collect();
//!   Ok(Some(vec))
//! }
//!
//! // custom merge function that merges two lists
//! // the new stage will append entries to the previous stage
//! // instead of replace it entirely
//! // you can return any error here that implements Display
//! fn merge_vec(left: &mut Option<Vec<String>>, right: Option<Vec<String>>) -> Result<(), std::convert::Infallible> {
//!   if let Some(left) = left.as_mut() {
//!     if let Some(mut right) = right {
//!       left.append(&mut right);
//!     }
//!   } else {
//!     *left = right
//!   }
//!
//!   Ok(())
//! }