1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
//! The [ConfigGenerator] derive macro is intended to simplify the process of writing
//! configuration loading boilerplate. It currently provides two main features:
//! TOML loading, and loading of values from environment variables.
//!
//! # Features
//!
//! By default, the `load_toml` feature is enabled, supplying a `with_toml` function implementation
//! on the input struct, and consequently requiring the [serde] and [toml] crates. This feature can
//! be disabled, and these crates removed, if the user so chooses.
//!
//! # Usage
//!
//! The macro is used by applying it to a struct via `#[derive(ConfigGenerator)`.
//!
//! ```
//! use config_generator::ConfigGenerator;
//!
//! #[derive(ConfigGenerator, Default)]
//! struct Config {
//! #[env_key = "NAME_ENV_KEY"]
//! pub name: String,
//! #[env_key = "THREAD_COUNT"]
//! pub thread_count: u32,
//! #[env_key = "ALLOWED_ORIGINS"]
//! pub allowed_origins: Vec<String>,
//! #[env_key = "ASSETS_PATH"]
//! pub assets_path: Option<String>,
//! }
//! ```
//!
//! This generates a private struct called `Optional[InputStruct]`. Thus, in the example above,
//! `OptionalConfig` would be generated. The generated optional struct is identical to the input
//! struct except that each of its fields is an [Option<T>]. This field is used internally to track
//! loaded data and apply it to the configuration struct when the user calls the `with_toml` or
//! `with_environment` functions to load values.
//!
//! Note that the user is expected to derive [Default] or implement some other means of obtaining
//! a starting point for configuration loading, such as a `new` function.
//!
//! For example, the following basic example loads values from an input toml file.
//! ```
//! use config_generator::ConfigGenerator;
//!
//! #[derive(ConfigGenerator, Default)]
//! struct Config {
//! pub name: String,
//! }
//!
//! fn main() {
//! let config = Config::default().with_toml(&"tests/tomls/simple_config.toml");
//! }
//! ```
//!
//! Similarly, the following example would load values from the environment, specifically
//! from the `NAME_KEY` environment variable annotated above the `name` field.
//! ```
//! use config_generator::ConfigGenerator;
//!
//! #[derive(ConfigGenerator, Default)]
//! struct Config {
//! #[env_key = "NAME_KEY"]
//! pub name: String,
//! }
//!
//! fn main() {
//! let config = Config::default().with_env();
//! }
//! ```
//!
//! These functions can also be composed together. When composed, a latter-executed function
//! will superimpose loaded values on the prior config.
//! ```
//! use config_generator::ConfigGenerator;
//!
//! #[derive(ConfigGenerator, Default)]
//! struct Config {
//! #[env_key = "NAME_KEY"]
//! pub name: String,
//! }
//!
//! fn main() {
//! let config = Config::default()
//! .with_toml(&"tests/tomls/simple_config.toml")
//! .with_env();
//! }
//! ```
//!
//! Superimposing values follows certain rules:
//! 1. If the original struct had an [Option<T>] field, the value for that field will _only_ be
//! replaced if the new value is [Some]. A subseqently-loaded [None] value will _not_ be applied.
//! 2. If the original struct had a [Vec<T>] field, subsequently-loaded values will be _combined_
//! with prior values, such that the [Vec] has each new value pushed to the end.
//! 3. For all other types, if a value is loaded, then that value will replace any previously-set value.
pub use ConfigGenerator;