1use crate::{ConfigError, ConfigLocation, ConfigType};
2use std::{fs::read_to_string, io::Write};
3
4pub fn load_json<'a, T>(
33 app_name: impl AsRef<str>,
34 config_name: impl Into<Option<&'a str>>,
35 location: impl AsRef<ConfigLocation>,
36 reset_conf_on_err: bool,
37) -> Result<T, ConfigError>
38where
39 T: Default + serde::Serialize + serde::de::DeserializeOwned,
40{
41 let config_file_path = crate::config_location(
42 app_name.as_ref(),
43 config_name.into(),
44 ConfigType::Json.as_str(),
45 location.as_ref(),
46 )?;
47
48 let save_default_conf = || {
49 let default_config = T::default();
50 let json_str = serde_json::to_string_pretty(&default_config)?;
51 crate::save_config_str(&config_file_path, &json_str)?;
52 Ok(default_config)
53 };
54
55 if !config_file_path.try_exists()? {
56 return save_default_conf();
57 }
58
59 let json_str = read_to_string(&config_file_path)?;
60 let config = match serde_json::from_str::<T>(&json_str) {
61 Ok(config) => config,
62 Err(err) => {
63 if reset_conf_on_err {
64 return save_default_conf();
65 }
66 return Err(err.into());
67 }
68 };
69
70 Ok(config)
71}
72
73pub fn store_json<'a, T>(
104 app_name: impl AsRef<str>,
105 config_name: impl Into<Option<&'a str>>,
106 location: impl AsRef<ConfigLocation>,
107 data: T,
108) -> Result<(), ConfigError>
109where
110 T: serde::Serialize,
111{
112 let config_file_path = crate::config_location(
113 app_name.as_ref(),
114 config_name.into(),
115 ConfigType::Json.as_str(),
116 location.as_ref(),
117 )?;
118
119 let mut file = std::io::BufWriter::new(std::fs::File::create(config_file_path)?);
120
121 let json_str = serde_json::to_string_pretty(&data)?;
122
123 file.write_all(json_str.as_bytes())?;
124
125 Ok(())
126}
127
128#[cfg(test)]
129mod tests {
130 use super::*;
131
132 use serde::Deserialize;
133 use ConfigLocation::{Cache, Config, Cwd, LocalData};
134
135 #[derive(Default, serde::Serialize, Deserialize, PartialEq, Debug, Clone)]
136 struct TestConfig {
137 test: String,
138 test_vec: Vec<u8>,
139 }
140
141 #[test]
142 fn read_default_config_json() {
143 let config = load_json::<TestConfig>(
144 "test-binconf-read_default_config-string-json",
145 None,
146 Config,
147 false,
148 )
149 .unwrap();
150 assert_eq!(config, TestConfig::default());
151
152 let test_config = TestConfig {
153 test: String::from("test"),
154 test_vec: vec![1, 2, 3, 4, 5],
155 };
156
157 let config: TestConfig = load_json(
158 "test-binconf-read_default_config-struct-json",
159 None,
160 Config,
161 false,
162 )
163 .unwrap();
164 assert_eq!(config, TestConfig::default());
165
166 store_json(
167 "test-binconf-read_default_config-struct-json",
168 None,
169 Config,
170 &test_config,
171 )
172 .unwrap();
173 let config: TestConfig = load_json(
174 "test-binconf-read_default_config-struct-json",
175 None,
176 Config,
177 false,
178 )
179 .unwrap();
180 assert_eq!(config, test_config);
181 }
182
183 #[test]
184 fn config_with_name_json() {
185 let config = load_json::<TestConfig>(
186 "test-binconf-config_with_name-string-json",
187 Some("test-config.json"),
188 Config,
189 false,
190 )
191 .unwrap();
192 assert_eq!(config, TestConfig::default());
193
194 let test_config = TestConfig {
195 test: String::from("test"),
196 test_vec: vec![1, 2, 3, 4, 5],
197 };
198
199 let config: TestConfig = load_json(
200 "test-binconf-config_with_name-struct-json",
201 Some("test-config.json"),
202 Config,
203 false,
204 )
205 .unwrap();
206 assert_eq!(config, TestConfig::default());
207
208 store_json(
209 "test-binconf-config_with_name-struct-json",
210 Some("test-config.json"),
211 Config,
212 &test_config,
213 )
214 .unwrap();
215 let config: TestConfig = load_json(
216 "test-binconf-config_with_name-struct-json",
217 Some("test-config.json"),
218 Config,
219 false,
220 )
221 .unwrap();
222 assert_eq!(config, test_config);
223 }
224
225 #[test]
226 fn returns_error_on_invalid_config_json() {
227 let data = TestConfig {
228 test: String::from("test"),
229 test_vec: vec![1, 2, 3, 4, 5],
230 };
231
232 store_json(
233 "test-binconf-returns_error_on_invalid_config-json",
234 None,
235 Config,
236 &data,
237 )
238 .unwrap();
239 let config = load_json::<String>(
240 "test-binconf-returns_error_on_invalid_config-json",
241 None,
242 Config,
243 false,
244 );
245
246 assert!(config.is_err());
247 }
248
249 #[test]
250 fn save_config_user_config_json() {
251 let data = TestConfig {
252 test: String::from("test"),
253 test_vec: vec![1, 2, 3, 4, 5],
254 };
255
256 store_json(
257 "test-binconf-save_config_user_config-json",
258 None,
259 Config,
260 &data,
261 )
262 .unwrap();
263 let config: TestConfig = load_json(
264 "test-binconf-save_config_user_config-json",
265 None,
266 Config,
267 false,
268 )
269 .unwrap();
270 assert_eq!(config, data);
271 }
272
273 #[test]
274 fn save_config_user_cache_json() {
275 let data = TestConfig {
276 test: String::from("test"),
277 test_vec: vec![1, 2, 3, 4, 5],
278 };
279
280 store_json(
281 "test-binconf-save_config_user_cache-json",
282 None,
283 Cache,
284 &data,
285 )
286 .unwrap();
287 let config: TestConfig = load_json(
288 "test-binconf-save_config_user_cache-json",
289 None,
290 Cache,
291 false,
292 )
293 .unwrap();
294 assert_eq!(config, data);
295 }
296
297 #[test]
298 fn save_config_user_local_data_json() {
299 let data = TestConfig {
300 test: String::from("test"),
301 test_vec: vec![1, 2, 3, 4, 5],
302 };
303
304 store_json(
305 "test-binconf-save_config_user_local_data-json",
306 None,
307 LocalData,
308 &data,
309 )
310 .unwrap();
311 let config: TestConfig = load_json(
312 "test-binconf-save_config_user_local_data-json",
313 None,
314 LocalData,
315 false,
316 )
317 .unwrap();
318 assert_eq!(config, data);
319 }
320
321 #[test]
322 fn save_config_user_cwd_json() {
323 let data = TestConfig {
324 test: String::from("test"),
325 test_vec: vec![1, 2, 3, 4, 5],
326 };
327
328 store_json("test-binconf-save_config_user_cwd-json", None, Cwd, &data).unwrap();
329 let config: TestConfig =
330 load_json("test-binconf-save_config_user_cwd-json", None, Cwd, false).unwrap();
331 assert_eq!(config, data);
332 }
333}