1use std::collections::HashMap;
28
29pub mod env;
30pub mod multi;
31pub mod simple;
32
33pub use env::Environment;
34pub use multi::MultiConfig;
35pub use simple::{Error, Simple};
36
37pub trait Config {
40 fn get(&self, key: &str) -> Option<String>;
42
43 fn must_get(&self, key: &str) -> String {
45 self.get(key).unwrap()
46 }
47
48 fn string(&self, key: &str) -> String {
50 self.get(key).unwrap()
51 }
52
53 fn int(&self, key: &str) -> i64 {
56 self.must_get(key).parse::<i64>().unwrap()
57 }
58
59 fn float(&self, key: &str) -> f64 {
62 self.must_get(key).parse::<f64>().unwrap()
63 }
64
65 fn bool(&self, key: &str) -> bool {
70 match self.must_get(key).to_lowercase().as_str() {
71 "t" => true,
72 "true" => true,
73 "1" => true,
74 "y" => true,
75 "yes" => true,
76 _ => false,
77 }
78 }
79
80 fn duration(&self, key: &str) -> chrono::Duration {
85 chrono::Duration::seconds(self.int(key))
88 }
89
90 fn datetime(&self, key: &str) -> chrono::DateTime<chrono::Utc> {
93 chrono::DateTime::<chrono::Utc>::from_utc(
94 chrono::DateTime::parse_from_rfc3339(self.must_get(key).as_str())
95 .unwrap()
96 .naive_utc(),
97 chrono::Utc,
98 )
99 }
100
101 fn list(&self, key: &str) -> Vec<String> {
105 let s = self.must_get(key);
106 let s = s.trim_matches(|c| c == '[' || c == ']' || char::is_whitespace(c));
107 s.split(',')
108 .map(|p| p.trim().to_string())
109 .collect::<Vec<String>>()
110 }
111
112 fn map(&self, key: &str) -> HashMap<String, String> {
117 let s = self.must_get(key);
118 let s = s.trim_matches(|c| c == '{' || c == '}' || char::is_whitespace(c));
119 s.split(',')
120 .map(|p| {
121 let parts = p.split("=>").map(|k| k.trim()).collect::<Vec<&str>>();
122 if parts.len() < 2 {
123 (parts[0], "")
124 } else {
125 (parts[0], parts[1])
126 }
127 })
128 .map(|(k, v)| (k.to_string(), v.to_string()))
129 .collect::<HashMap<String, String>>()
130 }
131}
132
133#[macro_export]
135macro_rules! default_config(
136 { $($key:expr => $value:expr),+ } => {
137 {
138 let mut m: ::std::collections::HashMap<&str, &str> = ::std::collections::HashMap::new();
139 $(
140 m.insert($key, $value);
141 )+
142 Box::new(m)
143 }
144 };
145);
146
147impl Config for HashMap<&str, &str> {
148 fn get(&self, key: &str) -> Option<String> {
149 match self.get(key) {
150 None => None,
151 Some(v) => Some(v.to_string()),
152 }
153 }
154}
155
156#[cfg(test)]
157mod tests {
158 use crate::*;
159 use chrono::{TimeZone, Utc};
160 use lazy_static::lazy_static;
161 use std::collections::HashMap;
162
163 #[test]
164 fn default() {
165 let config = default_config! {
166 "foo" => "bar",
167 "bar" => "baz",
168 "baz" => "foo"
169 };
170 assert_eq!(config.string("foo"), "bar".to_string());
171 }
172
173 #[test]
174 fn hash_map() {
175 use std::collections::HashMap;
176 let mut m = HashMap::new();
177 m.insert("foo", "bar");
178 assert_eq!(m.must_get("foo"), "bar".to_string());
179 assert!(m.get("bar").is_none());
180 }
181
182 lazy_static! {
183 static ref HASHMAP: HashMap<&'static str, &'static str> = {
184 let mut m = HashMap::new();
185 m.insert("foo", "bar");
186 m.insert("int", "100");
187 m.insert("float", "-2.4");
188 m.insert("bool", "t");
189 m.insert("duration", "50");
190 m.insert("datetime", "2015-05-15T05:05:05+00:00");
191 m.insert("list", "[1, 2, 3]");
192 m.insert("map", "{a=>1, b=>2, c=>3}");
193 m
194 };
195 }
196
197 macro_rules! test_gets {
198 ($(($name:ident, $test:expr): $exp:expr,)*) => {
199 $(
200 #[test]
201 fn $name() {
202 assert_eq!(
203 $test,
204 $exp
205 );
206 }
207
208 )*
209
210 }
211 }
212
213 test_gets! {
214 (string, HASHMAP.string("foo")): "bar".to_string(),
215 (int, HASHMAP.int("int")): 100,
216 (float, HASHMAP.float("float")): -2.4,
217 (bool, HASHMAP.bool("bool")): true,
218 (duration, HASHMAP.duration("duration")): chrono::Duration::seconds(50),
219 (datetime, HASHMAP.datetime("datetime")): Utc.ymd(2015, 5, 15).and_hms(5, 5, 5),
220 (list, HASHMAP.list("list")): vec!["1", "2", "3"],
221 (map, HASHMAP.map("map")): {
222 let mut m: HashMap<String, String> = HashMap::new();
223 m.insert("a".to_string(), "1".to_string());
224 m.insert("b".to_string(), "2".to_string());
225 m.insert("c".to_string(), "3".to_string());
226 m
227 },
228 }
229}