system_config/
lib.rs

1//! # system-config
2//! A Rust library for storing application properties on disk in any context and between restarts.
3//!
4//! # Example
5//! Add key-value pairs to the config and write it to disk...
6//! ```rust
7//! let mut config = Config::new("system-config-example").unwrap();
8//!
9//! config.insert("key1", "value1");
10//! config.insert("key2", "value2");
11//!
12//! config.write().unwrap();
13//! ```
14//!
15//! Then retrieve the information at any other time, even after the application is restarted or in different contexts.
16//! ```rust
17//! let config = Config::new("system-config-example").unwrap();
18//!
19//! let key1 = config.get("key1").unwrap();
20//! let key2 = config.get("key2").unwrap();
21//!
22//! println!("key1: {}", key1);
23//! println!("key2: {}", key2);
24//! ```
25
26use std::{collections::HashMap, fs, path::PathBuf};
27use anyhow::{Result, anyhow};
28
29#[derive(Clone, Debug, PartialEq)]
30pub struct Config {
31    internal: HashMap<String, String>,
32    pub path: PathBuf,
33}
34
35impl Config {
36    /// Create a new system config.
37    pub fn new<T: AsRef<str>>(name: T) -> Result<Self> {
38        let mut path = match dirs::config_dir() {
39            Some(data) => data,
40            None => return Err(anyhow!("Couldn't get config path")),
41        };
42
43        path.push(format!("{}.yaml", name.as_ref()));
44
45        let contents = match fs::read_to_string(&path) {
46            Ok(data) => data,
47            Err(_error) => {
48                let temp: HashMap<String, String> = HashMap::new();
49
50                let deserialized = serde_yaml::to_string(&temp)?;
51
52                fs::write(&path, deserialized.as_bytes())?;
53
54                deserialized
55            }
56        };
57
58        let internal: HashMap<String, String> = serde_yaml::from_str(contents.as_str())?;
59
60        let myself = Self{
61            internal,
62            path,
63        };
64
65        Ok(myself)
66    }
67
68    /// Clear a config by name and sync it with the disk.
69    pub fn write_clear_by_name<T: AsRef<str>>(name: T) -> Result<()> {
70        let mut config = Self::new(name)?;
71        
72        config.write_clear()
73    }
74
75    /// Update the config from disk.
76    pub fn read(&mut self) -> Result<()> {       
77        let contents = fs::read_to_string(&self.path)?;
78
79        let internal: HashMap<String, String> = serde_yaml::from_str(contents.as_str())?;
80
81        self.internal = internal;
82
83        Ok(())
84    }
85
86    /// Update the disk from the config.
87    pub fn write(&self) -> Result<()> {
88        let deserialized = serde_yaml::to_string(&self.internal)?;
89
90        fs::write(&self.path, deserialized.as_bytes())?;
91
92        Ok(())
93    }
94
95    /// Insert a key-value pair into the config.
96    pub fn insert<T: AsRef<str>>(&mut self, key: T, value: T) {
97        self.internal.insert(key.as_ref().to_string(), value.as_ref().to_string());
98    }
99
100    /// Get a value for a key.
101    pub fn get<T: AsRef<str>>(&self, query: T) -> Option<String> {
102        let res = match self.internal.get(&query.as_ref().to_string()) {
103            Some(data) => data,
104            None => return None,
105        };
106
107        Some(res.to_string())
108    }
109
110    /// Clear all data in the config.
111    pub fn clear(&mut self) {
112        self.internal.clear();
113    }
114
115    /// Insert a key-value pair into the config and write to disk.
116    pub fn write_insert<T: AsRef<str>>(&mut self, key: T, value: T) -> Result<()> {
117        self.insert(key, value);
118
119        self.write()
120    }
121
122    /// Read the system config and get a value for a key.
123    pub fn read_get<T: AsRef<str>>(&mut self, query: T) -> Result<Option<String>> {
124        self.read()?;
125
126        Ok(self.get(query))
127    }
128
129    /// Clear all data in the config and write to disk.
130    pub fn write_clear(&mut self) -> Result<()> {
131        self.clear();
132
133        self.write()
134    }
135}