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