1 use async_trait::async_trait;
2 use config::{AsyncSource, Config, ConfigError, FileFormat, Format, Map, Value};
3 use std::{env, fs, path, str::FromStr};
4 use tokio::fs::read_to_string;
5 
6 #[derive(Debug)]
7 struct AsyncFile {
8     path: String,
9     format: FileFormat,
10 }
11 
12 /// This is a test only implementation to be used in tests
13 impl AsyncFile {
new(path: String, format: FileFormat) -> Self14     pub fn new(path: String, format: FileFormat) -> Self {
15         Self { path, format }
16     }
17 }
18 
19 #[async_trait]
20 impl AsyncSource for AsyncFile {
collect(&self) -> Result<Map<String, Value>, ConfigError>21     async fn collect(&self) -> Result<Map<String, Value>, ConfigError> {
22         let mut path = env::current_dir().unwrap();
23         let local = path::PathBuf::from_str(&self.path).unwrap();
24 
25         path.extend(local.iter());
26         let path = fs::canonicalize(path).map_err(|e| ConfigError::Foreign(Box::new(e)))?;
27 
28         let text = read_to_string(path)
29             .await
30             .map_err(|e| ConfigError::Foreign(Box::new(e)))?;
31 
32         self.format
33             .parse(Some(&self.path), &text)
34             .map_err(|e| ConfigError::Foreign(e))
35     }
36 }
37 
38 #[tokio::test]
test_single_async_file_source()39 async fn test_single_async_file_source() {
40     let config = Config::builder()
41         .add_async_source(AsyncFile::new(
42             "tests/Settings.json".to_owned(),
43             FileFormat::Json,
44         ))
45         .build()
46         .await
47         .unwrap();
48 
49     assert!(config.get::<bool>("debug").unwrap());
50 }
51 
52 #[tokio::test]
test_two_async_file_sources()53 async fn test_two_async_file_sources() {
54     let config = Config::builder()
55         .add_async_source(AsyncFile::new(
56             "tests/Settings.json".to_owned(),
57             FileFormat::Json,
58         ))
59         .add_async_source(AsyncFile::new(
60             "tests/Settings.toml".to_owned(),
61             FileFormat::Toml,
62         ))
63         .build()
64         .await
65         .unwrap();
66 
67     assert_eq!("Torre di Pisa", config.get::<String>("place.name").unwrap());
68     assert!(config.get::<bool>("debug_json").unwrap());
69     assert_eq!(1, config.get::<i32>("place.number").unwrap());
70 }
71 
72 #[tokio::test]
test_sync_to_async_file_sources()73 async fn test_sync_to_async_file_sources() {
74     let config = Config::builder()
75         .add_source(config::File::new("tests/Settings", FileFormat::Json))
76         .add_async_source(AsyncFile::new(
77             "tests/Settings.toml".to_owned(),
78             FileFormat::Toml,
79         ))
80         .build()
81         .await
82         .unwrap();
83 
84     assert_eq!("Torre di Pisa", config.get::<String>("place.name").unwrap());
85     assert_eq!(1, config.get::<i32>("place.number").unwrap());
86 }
87 
88 #[tokio::test]
test_async_to_sync_file_sources()89 async fn test_async_to_sync_file_sources() {
90     let config = Config::builder()
91         .add_async_source(AsyncFile::new(
92             "tests/Settings.toml".to_owned(),
93             FileFormat::Toml,
94         ))
95         .add_source(config::File::new("tests/Settings", FileFormat::Json))
96         .build()
97         .await
98         .unwrap();
99 
100     assert_eq!("Torre di Pisa", config.get::<String>("place.name").unwrap());
101     assert_eq!(1, config.get::<i32>("place.number").unwrap());
102 }
103 
104 #[tokio::test]
test_async_file_sources_with_defaults()105 async fn test_async_file_sources_with_defaults() {
106     let config = Config::builder()
107         .set_default("place.name", "Tower of London")
108         .unwrap()
109         .set_default("place.sky", "blue")
110         .unwrap()
111         .add_async_source(AsyncFile::new(
112             "tests/Settings.toml".to_owned(),
113             FileFormat::Toml,
114         ))
115         .build()
116         .await
117         .unwrap();
118 
119     assert_eq!("Torre di Pisa", config.get::<String>("place.name").unwrap());
120     assert_eq!("blue", config.get::<String>("place.sky").unwrap());
121     assert_eq!(1, config.get::<i32>("place.number").unwrap());
122 }
123 
124 #[tokio::test]
test_async_file_sources_with_overrides()125 async fn test_async_file_sources_with_overrides() {
126     let config = Config::builder()
127         .set_override("place.name", "Tower of London")
128         .unwrap()
129         .add_async_source(AsyncFile::new(
130             "tests/Settings.toml".to_owned(),
131             FileFormat::Toml,
132         ))
133         .build()
134         .await
135         .unwrap();
136 
137     assert_eq!(
138         "Tower of London",
139         config.get::<String>("place.name").unwrap()
140     );
141     assert_eq!(1, config.get::<i32>("place.number").unwrap());
142 }
143