cfg_rs/prelude.rs
1use std::borrow::Borrow;
2
3use crate::{ConfigError, ConfigValue, Configuration, FromConfig};
4
5/// Test macro to generate config instance from a static map of key-value pairs.
6///
7/// Macro to generate config instance from a map of key-value pairs.
8/// The keys in the map are config keys, e.g. "port".
9/// The values in the map are string values, e.g. "8080".
10/// This macro will panic if any required config is missing or
11/// if any config value cannot be parsed into the expected type.
12/// # Example
13/// ```rust
14/// use cfg_rs::*;
15/// #[derive(Debug, FromConfig)]
16/// struct AppConfig {
17/// port: u16,
18/// host: String,
19/// }
20/// let config: AppConfig = from_static_map!(AppConfig, {
21/// "port" => "8080",
22/// "host" => "localhost",
23/// });
24/// assert_eq!(config.port, 8080);
25/// assert_eq!(config.host, "localhost");
26/// ```
27/// Note: This macro is intended for use in tests or examples where
28/// you want to quickly create a config instance from inline key-value pairs.
29/// It is not recommended for use in production code.
30#[macro_export]
31macro_rules! from_static_map {
32 ( $ty:ty, { $( $key:expr => $value:expr ),* $(,)? } ) => {{
33 use $crate::*;
34 use std::collections::HashMap;
35 let mut config: HashMap<String, String> = HashMap::new();
36 $(
37 config.insert($key.to_string(), $value.to_string());
38 )*
39 from_map::<$ty, _, _, _>(config, "").expect("from_static_map failed")
40 }};
41}
42
43/// Generate config instance from a map of key-value pairs.
44///
45/// The keys in the map are full config keys, e.g. "cfg.app.port".
46/// The values in the map are string values, e.g. "8080".
47/// The `prefix` is used to scope the config keys, e.g. "cfg.app".
48/// This function will return an error if any required config is missing or
49/// if any config value cannot be parsed into the expected type.
50/// # Example
51/// ```rust
52/// use std::collections::HashMap;
53/// use cfg_rs::*;
54/// #[derive(Debug, FromConfig)]
55/// struct AppConfig {
56/// port: u16,
57/// host: String,
58/// }
59/// let mut map = HashMap::new();
60/// map.insert("cfg.app.port", "8080");
61/// map.insert("cfg.app.host", "localhost");
62/// let config: AppConfig = from_map(map, "cfg.app").unwrap();
63/// assert_eq!(config.port, 8080);
64/// assert_eq!(config.host, "localhost");
65/// ```
66#[allow(unused_mut)]
67pub fn from_map<
68 T: FromConfig,
69 I: IntoIterator<Item = (K, V)>,
70 K: Borrow<str>,
71 V: Into<ConfigValue<'static>>,
72>(
73 map: I,
74 prefix: &str,
75) -> Result<T, ConfigError> {
76 let mut config = Configuration::new().register_kv("default");
77 for (k, v) in map {
78 config = config.set(k, v);
79 }
80 let mut config = config.finish()?;
81 #[cfg(feature = "rand")]
82 {
83 config = config.register_random()?;
84 }
85 config.get(prefix)
86}
87
88/// Generate config instance from environment variables.
89///
90/// The `prefix` is used to scope the config keys, e.g. "CFG_APP".
91/// This function will return an error if any required config is missing or
92/// if any config value cannot be parsed into the expected type.
93/// # Example
94/// ```rust
95/// use cfg_rs::*;
96/// #[derive(Debug, FromConfig)]
97/// struct AppConfig {
98/// port: u16,
99/// host: String,
100/// }
101/// unsafe {
102/// std::env::set_var("CFG_APP_PORT", "8080");
103/// std::env::set_var("CFG_APP_HOST", "localhost");
104/// }
105/// let config: AppConfig = from_env("CFG_APP").unwrap();
106/// assert_eq!(config.port, 8080);
107/// assert_eq!(config.host, "localhost");
108/// ```
109#[allow(unused_mut)]
110pub fn from_env<T: FromConfig>(prefix: &str) -> Result<T, ConfigError> {
111 let mut config = Configuration::new().register_prefix_env(prefix)?;
112 #[cfg(feature = "rand")]
113 {
114 config = config.register_random()?;
115 }
116 config.get("")
117}
118
119#[cfg_attr(coverage_nightly, coverage(off))]
120#[cfg(test)]
121mod tests {
122 use super::*;
123 use crate::FromConfig;
124 use std::collections::HashMap;
125
126 #[derive(Debug, PartialEq, FromConfig)]
127 #[config(crate = "crate")]
128 struct TestApp {
129 port: u16,
130 host: String,
131 }
132
133 #[test]
134 fn test_from_map_happy_path() {
135 let mut map = HashMap::new();
136 map.insert("cfg.app.port", "8080");
137 map.insert("cfg.app.host", "localhost");
138
139 let cfg: TestApp = from_map(map, "cfg.app").expect("from_map failed");
140 assert_eq!(
141 cfg,
142 TestApp {
143 port: 8080,
144 host: "localhost".to_string()
145 }
146 );
147 }
148
149 #[test]
150 fn test_from_env_happy_path() {
151 // Use a unique prefix to avoid colliding with other env vars
152 let prefix = "TEST_APP";
153 unsafe {
154 std::env::set_var("TEST_APP_PORT", "9090");
155 std::env::set_var("TEST_APP_HOST", "127.0.0.1");
156 }
157
158 let cfg: TestApp = from_env(prefix).expect("from_env failed");
159 assert_eq!(
160 cfg,
161 TestApp {
162 port: 9090,
163 host: "127.0.0.1".to_string()
164 }
165 );
166
167 // Clean up
168 unsafe {
169 std::env::remove_var("TEST_APP_PORT");
170 std::env::remove_var("TEST_APP_HOST");
171 }
172 }
173
174 #[test]
175 fn test_load_from_map_macro_happy_path() {
176 // Use the macro to construct TestApp from inline kvs
177 let app: TestApp = from_static_map!(TestApp, {
178 "port" => "8080",
179 "host" => "localhost",
180 });
181
182 assert_eq!(
183 app,
184 TestApp {
185 port: 8080,
186 host: "localhost".to_string()
187 }
188 );
189 }
190
191 #[test]
192 fn test_load_from_map_macro_single_entry() {
193 // Single entry form should also work
194 // host is missing so deriving FromConfig would error; instead test getting a struct with only port
195 #[derive(Debug, PartialEq, FromConfig)]
196 #[config(crate = "crate")]
197 struct OnlyPort {
198 port: u16,
199 }
200 let only: OnlyPort = from_static_map!(OnlyPort, { "port" => "7070" });
201 assert_eq!(only, OnlyPort { port: 7070 });
202 }
203}