Skip to main content

app_json_settings/
lib.rs

1//! App JSON Settings
2
3use serde::{Deserialize, Serialize};
4use serde_json::Value;
5
6use std::fs::{read_to_string, remove_dir, remove_file, File};
7use std::io::Write;
8use std::path::PathBuf;
9
10use dir::{config_dir_filepath, exe_dir_filepath, json_load};
11
12mod dir;
13#[cfg(test)]
14mod tests;
15
16/// default file name
17const DEFAULT_FILENAME: &str = "settings.json";
18
19/// core
20#[derive(Serialize, Deserialize)]
21pub struct JsonSettings {
22    filepath: PathBuf,
23}
24
25/// i/o
26#[derive(Serialize, Deserialize)]
27pub struct KeyValue {
28    key: Option<String>,
29    pub value: Option<Value>,
30    file_exists: bool,
31    key_exists: bool,
32}
33
34impl JsonSettings {
35    /// create instance
36    pub fn new(filepath: &PathBuf) -> JsonSettings {
37        JsonSettings {
38            filepath: filepath.to_owned(),
39        }
40    }
41
42    /// create instance to manage file in executable dir
43    pub fn exe_dir() -> JsonSettings {
44        let filepath = exe_dir_filepath(DEFAULT_FILENAME);
45        JsonSettings::new(&filepath)
46    }
47
48    /// create instance to manage file in executable dir
49    pub fn exe_dir_with_filename(filename: &str) -> JsonSettings {
50        let filepath = exe_dir_filepath(filename);
51        JsonSettings::new(&filepath)
52    }
53
54    /// create instance to manage file in user config dir
55    pub fn config_dir() -> JsonSettings {
56        let filepath = config_dir_filepath(DEFAULT_FILENAME);
57        JsonSettings::new(&filepath)
58    }
59
60    /// create instance to manage file in user config dir
61    pub fn config_dir_with_filename(filename: &str) -> JsonSettings {
62        let filepath = config_dir_filepath(filename);
63        JsonSettings::new(&filepath)
64    }
65
66    /// get value from key if exists
67    pub fn read_by_key(&self, key: &str) -> Result<KeyValue, Box<dyn std::error::Error>> {
68        let filepath = &self.filepath;
69
70        if !filepath.exists() {
71            return Ok(KeyValue {
72                key: None,
73                value: None,
74                file_exists: false,
75                key_exists: false,
76            });
77        }
78
79        let json = json_load(&filepath)?;
80        if let Some(value) = json.get(key) {
81            Ok(KeyValue {
82                key: Some(key.to_owned()),
83                value: Some(value.to_owned()),
84                file_exists: true,
85                key_exists: true,
86            })
87        } else {
88            return Ok(KeyValue {
89                key: Some(key.to_owned()),
90                value: None,
91                file_exists: true,
92                key_exists: false,
93            });
94        }
95    }
96
97    /// append or update value bound to key
98    pub fn write_by_key(&self, key: &str, value: &Value) -> Result<(), std::io::Error> {
99        let filepath = &self.filepath;
100
101        let mut current_settings = if filepath.exists() {
102            let file_text = read_to_string(&filepath)?;
103            serde_json::from_str(&file_text).unwrap_or_default()
104        } else {
105            Value::Object(serde_json::Map::new())
106        };
107
108        let map = current_settings.as_object_mut().unwrap();
109        map.insert(key.to_owned(), value.to_owned());
110
111        let updated_settings = serde_json::to_string_pretty(&current_settings)?;
112
113        let mut file = File::create(&filepath)?;
114        file.write_all(updated_settings.as_bytes())?;
115
116        Ok(())
117    }
118
119    /// remove settings file
120    pub fn remove(&self, remove_dir_if_empty: bool) {
121        remove_file(&self.filepath).expect("Failed to remove settings file");
122        if !remove_dir_if_empty {
123            match remove_dir(self.filepath.parent().unwrap()) {
124                Ok(_) => (),
125                Err(_) => (), // dir is not empty
126            }
127        }
128    }
129}