1pub use config::*;
3pub use duration_string::DurationString;
4pub use secrecy::*;
5use serde::Deserialize;
6
7pub mod kafka;
8pub mod postgres;
9pub mod redis;
10
11pub fn load_yaml<'a, T: serde::de::Deserialize<'a>>(file: &str) -> Result<T, ConfigError> {
12 load(file, FileFormat::Yaml)
13}
14
15#[derive(Deserialize)]
16#[serde(deny_unknown_fields)]
17struct BaseConfig<T> {
18 #[serde(flatten)]
19 config: T,
20}
21
22pub fn load<'a, T: serde::de::Deserialize<'a>>(
23 file: &str,
24 format: FileFormat,
25) -> Result<T, ConfigError> {
26 let builder = Config::builder();
27 let base: BaseConfig<T> = match std::env::var("CONFIG_PATH") {
28 Ok(env_file_path) => builder.add_source(File::with_name(&env_file_path)),
29 Err(_) => builder.add_source(File::from_str(file, format)),
30 }
31 .add_source(
32 Environment::default()
33 .separator("__")
34 .prefix("cfg")
35 .prefix_separator("__"),
36 )
37 .build()?
38 .try_deserialize()?;
39 Ok(base.config)
40}
41
42#[cfg(test)]
43mod tests {
44 use config::FileFormat;
45 use std::{collections::HashMap, env};
46
47 use crate::load;
48
49 #[test]
50 fn test_load_yaml() {
51 use serde::Deserialize;
52
53 #[derive(Debug, Deserialize)]
54 struct Author {
55 pub name: String,
56 pub books: Vec<String>,
57 }
58
59 #[derive(Debug, Deserialize)]
60 struct Test {
61 pub count: u32,
62 pub author: Author,
63 }
64
65 let expected_data: Test = Test {
66 count: 10,
67 author: Author {
68 name: "John Ronald Reuel Tolkien".to_string(),
69 books: vec!["The Lord of the Rings".to_string()],
70 },
71 };
72
73 let data: Test = load(include_str!("./test_config.yaml"), FileFormat::Yaml).unwrap();
74
75 assert_eq!(data.count, expected_data.count);
76 assert_eq!(data.author.name, expected_data.author.name);
77 assert_eq!(data.author.books, expected_data.author.books);
78 }
79
80 #[test]
81 fn test_load_yaml_with_secrets() {
82 use secrecy::{ExposeSecret, SecretString};
83 use serde::Deserialize;
84
85 #[derive(Debug, Deserialize)]
86 struct Config {
87 pub login: String,
88 pub password: SecretString,
89 }
90 let expected_data = Config {
91 login: "pepe".to_string(),
92 password: SecretString::from("the_frog"),
93 };
94
95 let data: Config = load("login: pepe\npassword: the_frog", FileFormat::Yaml).unwrap();
96 assert_eq!(data.login, expected_data.login);
97 assert_eq!(
98 *data.password.expose_secret(),
99 *expected_data.password.expose_secret()
100 );
101 }
102
103 #[test]
104 fn test_load_yaml_map() {
105 use serde::Deserialize;
106
107 #[derive(Debug, Deserialize)]
108 struct Config {
109 pub words_dict: HashMap<String, String>,
110 pub digits_dict: HashMap<String, String>,
111 }
112 let expected_data = Config {
113 words_dict: HashMap::from_iter(vec![
114 ("a".to_owned(), "a".to_owned()),
115 ("b".to_owned(), "B".to_owned()),
116 ("C".to_owned(), "c".to_owned()),
117 ("D".to_owned(), "D".to_owned()),
118 ]),
119 digits_dict: HashMap::from_iter(vec![
120 ("1".to_owned(), "one".to_owned()),
121 ("2".to_owned(), "two".to_owned()),
122 ]),
123 };
124
125 let data: Config = load(
126 "words_dict:\n a: a\n b: B\n C: c\n D: D\ndigits_dict:\n 1: one\n 2: two",
127 FileFormat::Yaml,
128 )
129 .unwrap();
130 assert_eq!(data.words_dict, expected_data.words_dict);
131 assert_eq!(data.digits_dict, expected_data.digits_dict);
132 }
133
134 #[test]
135 #[ignore = "can't be running on multithread due to env usage"]
136 fn test_env_string_override() {
137 use serde::Deserialize;
138
139 #[derive(Debug, Deserialize)]
140 #[serde(deny_unknown_fields)]
141 struct Author {
142 pub name: String,
143 pub books: Vec<String>,
144 }
145
146 #[derive(Debug, Deserialize)]
147 #[serde(deny_unknown_fields)]
148 struct Test {
149 pub count: u32,
150 pub author: Author,
151 }
152
153 let expected_data: Test = Test {
154 count: 10,
155 author: Author {
156 name: "Fyodor Dostoevsky".to_string(),
157 books: vec!["The Lord of the Rings".to_string()],
158 },
159 };
160
161 env::set_var("cfg__author__name", "Fyodor Dostoevsky");
162 let data: Test = load(include_str!("./test_config.yaml"), FileFormat::Yaml).unwrap();
163 env::remove_var("cfg__author__name");
164
165 assert_eq!(data.count, expected_data.count);
166 assert_eq!(data.author.name, expected_data.author.name);
167 assert_eq!(data.author.books, expected_data.author.books);
168 }
169}