1#![allow(dead_code)]
2extern crate core;
3
4mod config_source;
5mod error;
6mod utils;
7
8pub mod serde_utils;
9pub(crate) mod watcher;
10
11pub use {
12 self::config_source::{
13 new_config, Case, ConfigSource, CustomConfigSource, EnvSourceOptions, FileSourceOptions,
14 NorthConfig, NorthConfigOptions,
15 },
16 self::error::Error,
17};
18
19#[cfg(test)]
20mod tests {
21 use crate::{ConfigSource, EnvSourceOptions, NorthConfigOptions};
22
23 #[derive(Clone, serde::Deserialize, Debug)]
24 struct NestedConfig {
25 pub foo: String,
26 pub bar: String,
27 }
28
29 #[derive(Clone, serde::Deserialize, Debug)]
30 struct DemoConfig {
31 pub host: Option<String>,
32 pub nested: NestedConfig,
33 }
34
35 #[derive(Clone, serde::Deserialize, Debug)]
36 struct SimpleDemoConfig {
37 pub host: Option<String>,
38 }
39
40 fn setup_env() {
41 destroy_env();
42 envmnt::set("NORTH_HOST", "address");
43 envmnt::set("NORTH_NESTED__FOO", "env foo");
44 envmnt::set("NORTH_NESTED__BAR", "env bar");
45 }
46
47 fn destroy_env() {
48 envmnt::remove("NORTH_HOST");
49 envmnt::remove("NORTH_NESTED__FOO");
50 envmnt::remove("NORTH_NESTED__BAR");
51 }
52
53 #[cfg(feature = "tokio")]
54 #[tokio::test]
55 async fn reads_config_from_file_with_release_mode() {
56 let config_options = NorthConfigOptions {
57 sources: vec![ConfigSource::File(
58 "../../examples/configs/test.{{env}}.json".to_string(),
59 None,
60 )],
61 };
62 let config = crate::new_config::<DemoConfig>(config_options).await;
63 let config = config.get_value().clone();
64 let host = config.host.unwrap();
65 assert_eq!(host, "0.0.0.245");
66 }
67
68 #[cfg(feature = "async-std")]
69 #[async_std::test]
70 async fn reads_config_from_file_with_release_mode() {
71 let config_options = NorthConfigOptions {
72 sources: vec![ConfigSource::File(
73 "../../examples/configs/test.{{env}}.json".to_string(),
74 )],
75 };
76 let config = crate::new_config::<DemoConfig>(config_options).await;
77 let config = config.get_value().clone();
78 let host = config.host.unwrap();
79 assert_eq!(host, "0.0.0.245");
80 }
81
82 #[cfg(not(any(feature = "tokio", feature = "async-std")))]
83 #[test]
84 fn reads_config_from_file_with_release_mode() {
85 let config_options = NorthConfigOptions {
86 sources: vec![ConfigSource::File(
87 "../../examples/configs/test.{{env}}.json".to_string(),
88 None,
89 )],
90 };
91 let config = crate::new_config::<DemoConfig>(config_options);
92 let config = config.get_value().clone();
93 assert_eq!(config.host.unwrap(), "0.0.0.245");
94 }
95
96 #[cfg(feature = "tokio")]
97 #[tokio::test]
98 async fn reads_config_from_file() {
99 let config_options = NorthConfigOptions {
100 sources: vec![ConfigSource::File(
101 "../../examples/configs/test.debug.json".to_string(),
102 None,
103 )],
104 };
105 let config = crate::new_config::<DemoConfig>(config_options).await;
106 let config = config.get_value().clone();
107 let host = config.host.unwrap();
108 assert_eq!(host, "0.0.0.245");
109 }
110
111 #[cfg(feature = "async-std")]
112 #[async_std::test]
113 async fn reads_config_from_file() {
114 let config_options = NorthConfigOptions {
115 sources: vec![ConfigSource::File(
116 "../../examples/configs/test.debug.json".to_string(),
117 )],
118 };
119 let config = crate::new_config::<DemoConfig>(config_options).await;
120 let config = config.get_value().clone();
121 let host = config.host.unwrap();
122 assert_eq!(host, "0.0.0.245");
123 }
124
125 #[cfg(not(any(feature = "tokio", feature = "async-std")))]
126 #[test]
127 fn reads_config_from_file() {
128 let config_options = NorthConfigOptions {
129 sources: vec![ConfigSource::File(
130 "../../examples/configs/test.debug.json".to_string(),
131 None,
132 )],
133 };
134 let config = crate::new_config::<DemoConfig>(config_options);
135 let config = config.get_value().clone();
136 assert_eq!(config.host.unwrap(), "0.0.0.245");
137 }
138
139 #[cfg(feature = "tokio")]
140 #[tokio::test]
141 async fn merge_and_overwrite_duplicate_keys_from_two_config_file() {
142 let config_options = NorthConfigOptions {
143 sources: vec![
144 ConfigSource::File("../../examples/configs/test.debug.json".to_string(), None),
145 ConfigSource::File("../../examples/configs/test.release.json".to_string(), None),
146 ],
147 };
148 let config = crate::new_config::<DemoConfig>(config_options).await;
149 let config = config.get_value().clone();
150 let host = config.host.unwrap();
151 assert_eq!(host, "0.0.0.0");
152 }
153
154 #[cfg(not(any(feature = "tokio", feature = "async-std")))]
155 #[test]
156 fn merge_and_overwrite_duplicate_keys_from_two_config_file() {
157 let config_options = NorthConfigOptions {
158 sources: vec![
159 ConfigSource::File("../../examples/configs/test.debug.json".to_string(), None),
160 ConfigSource::File("../../examples/configs/test.release.json".to_string(), None),
161 ],
162 };
163 let config = crate::new_config::<DemoConfig>(config_options);
164 let config = config.get_value().clone();
165 assert_eq!(config.host.unwrap(), "0.0.0.0");
166 }
167
168 #[cfg(feature = "yaml")]
169 #[test]
170 fn merge_two_files_from_json_and_yaml_sources() {
171 let config_options = NorthConfigOptions {
172 sources: vec![
173 ConfigSource::File("../../examples/configs/test.release.json".to_string(), None),
174 ConfigSource::File("../../examples/configs/test.release.yaml".to_string(), None),
175 ],
176 };
177 let config = crate::new_config::<DemoConfig>(config_options);
178 let config = config.get_value().clone();
179 let host = config.host.unwrap();
180 assert_eq!(host, "127.0.0.1");
181 }
182
183 #[cfg(all(feature = "yaml", feature = "toml"))]
184 #[test]
185 fn merge_three_files_from_json_toml_and_yaml_sources() {
186 let config_options = NorthConfigOptions {
187 sources: vec![
188 ConfigSource::File("../../examples/configs/test.release.json".to_string(), None),
189 ConfigSource::File("../../examples/configs/test.release.yaml".to_string(), None),
190 ConfigSource::File("../../examples/configs/test.release.toml".to_string(), None),
191 ],
192 };
193 let config = crate::new_config::<DemoConfig>(config_options);
194 let config = config.get_value().clone();
195 let host = config.host.unwrap();
196 assert_eq!(host, "test");
197 }
198
199 #[test]
200 fn merge_deep_nested_objects() {
201 let config_options = NorthConfigOptions {
202 sources: vec![
203 ConfigSource::File("../../examples/configs/test.release.json".to_string(), None),
204 ConfigSource::File("../../examples/configs/test.debug.json".to_string(), None),
205 ],
206 };
207 let config = crate::new_config::<DemoConfig>(config_options);
208 let config = config.get_value().clone();
209 let host = config.host.unwrap();
210 let nested = config.nested;
211
212 assert_eq!(host, "0.0.0.245");
213 assert_eq!(nested.foo, "Well its foo");
214 }
215
216 #[test]
217 fn merge_env_and_file_sources() {
218 destroy_env();
219 envmnt::set("NORTH_HOST", "address");
220 envmnt::set("NORTH_NESTED__FOO", "env foo");
221
222 let config_options = NorthConfigOptions {
223 sources: vec![
224 ConfigSource::File("../../examples/configs/test.release.json".to_string(), None),
225 ConfigSource::File("../../examples/configs/test.debug.json".to_string(), None),
226 ConfigSource::Env(EnvSourceOptions{
227 prefix: Some("NORTH_".to_string()),
228 ..Default::default()
229 }),
230 ],
231 };
232 let config = crate::new_config::<DemoConfig>(config_options);
233 let config = config.get_value().clone();
234 let host = config.host.unwrap();
235 let nested = config.nested;
236
237 assert_eq!(host, "address");
238 assert_eq!(nested.foo, "env foo");
239 assert_eq!(nested.bar, "env bar");
240
241 envmnt::remove("NORTH_HOST");
242 envmnt::remove("NORTH_NESTED__FOO");
243 destroy_env();
244 }
245
246 #[test]
247 fn read_deep_config_from_env_sources_default_prefix() {
248 setup_env();
249
250 let config_options = NorthConfigOptions {
251 sources: vec![ConfigSource::Env(EnvSourceOptions::default())],
252 };
253 let config = crate::new_config::<DemoConfig>(config_options);
254 let config = config.get_value().clone();
255 let host = config.host.unwrap();
256 let nested = config.nested;
257
258 assert_eq!(host, "address");
259 assert_eq!(nested.foo, "env foo");
260 assert_eq!(nested.bar, "env bar");
261
262 destroy_env();
263 }
264
265 #[test]
266 fn read_deep_config_from_env_sources_custom_prefix() {
267 envmnt::set("TESTNORTH_HOST", "address");
268 envmnt::set("TESTNORTH_NESTED__FOO", "env foo 2");
269 envmnt::set("TESTNORTH_NESTED__BAR", "env bar 2");
270
271 let mut env_opts = EnvSourceOptions::default();
272 env_opts.prefix = Some("TESTNORTH".to_string());
273
274 let config_options = NorthConfigOptions {
275 sources: vec![ConfigSource::Env(env_opts)],
276 };
277
278 let config = crate::new_config::<DemoConfig>(config_options);
279 let config = config.get_value().clone();
280 let host = config.host.unwrap();
281 let nested = config.nested;
282
283 assert_eq!(host, "address");
284 assert_eq!(nested.foo, "env foo 2");
285 assert_eq!(nested.bar, "env bar 2");
286
287 envmnt::remove("TESTNORTH_HOST");
288 envmnt::remove("TESTNORTH_NESTED__FOO");
289 envmnt::remove("TESTNORTH_NESTED__BAR");
290 }
291
292 #[test]
293 fn read_deep_array_config_from_env_sources() {
294 #[derive(Clone, serde::Deserialize, serde::Serialize, Debug)]
295 struct Names {
296 pub first: i32
297 }
298 #[derive(Clone, serde::Deserialize, serde::Serialize, Debug)]
299 struct TestStr {
300 pub names: Vec<Names>
301 }
302
303 envmnt::set("NORTH_NAMES__0__FIRST", "50");
304
305 let mut env_opts = EnvSourceOptions::default();
306 env_opts.prefix = Some("NORTH".to_string());
307
308 let config_options = NorthConfigOptions {
309 sources: vec![ConfigSource::Env(env_opts)],
310 };
311
312 let config = crate::new_config::<TestStr>(config_options);
313 let config = config.get_value().clone();
314 let names = config.names;
315
316 assert_eq!(names.len(), 1);
317 assert_eq!(names[0].first, 50);
318
319 envmnt::remove("NORTH_NAMES__0__FIRST");
320 }
321
322 #[cfg(feature = "ron")]
323 #[test]
324 fn read_config_from_ron_source() {
325 let config_options = NorthConfigOptions {
326 sources: vec![ConfigSource::File(
327 "../../examples/configs/test.release.ron".to_string(),
328 None
329 )],
330 };
331 let config = crate::new_config::<DemoConfig>(config_options);
332 let config = config.get_value().clone();
333 let host = config.host.unwrap();
334
335 assert_eq!(host, "ron-host");
336 }
337
338 }