app_json_settings/
core.rs1use serde::{de::DeserializeOwned, Serialize};
2
3use std::fs;
4use std::io;
5use std::marker::PhantomData;
6use std::path::{Path, PathBuf};
7
8use crate::core::constant::DEFAULT_FILE_NAME;
9use crate::core::dir::default_config_dir;
10#[cfg(all(windows, feature = "uwp"))]
11use crate::core::dir::uwp_local_folder_dir;
12use crate::core::json::JsonFormat;
13use crate::ConfigError;
14use crate::Result;
15
16pub mod constant;
17mod dir;
18pub mod error;
19mod json;
20
21#[cfg(test)]
22mod tests;
23
24pub struct ConfigManager<T> {
25 folder_path: PathBuf,
26 file_name: String,
27 json_format: JsonFormat,
28 _marker: PhantomData<T>,
29}
30
31impl<T> ConfigManager<T>
32where
33 T: Serialize + DeserializeOwned,
34{
35 pub fn new() -> Self {
40 let app_name = std::env::current_exe()
41 .ok()
42 .and_then(|path| path.file_stem().map(|name| name.to_string_lossy().to_string()))
43 .unwrap_or_else(|| "app".to_string());
44
45 let folder_path = default_config_dir().join(&app_name);
46
47 Self {
48 folder_path,
49 file_name: DEFAULT_FILE_NAME.to_string(),
50 json_format: JsonFormat::Pretty,
51 _marker: PhantomData,
52 }
53 }
54
55 pub fn at_current_dir(mut self) -> Self {
57 self.folder_path = std::env::current_dir().unwrap_or_else(|_| PathBuf::from("."));
58 self
59 }
60
61 pub fn with_root_dir<P: Into<PathBuf>>(mut self, path: P) -> Self {
66 self.folder_path = path.into();
67 self
68 }
69
70 pub fn at_custom_dir<P: Into<PathBuf>>(self, path: P) -> Self {
74 self.with_root_dir(path)
75 }
76
77 #[cfg(all(windows, feature = "uwp"))]
81 pub fn at_uwp_local_folder(mut self) -> Result<Self> {
82 self.folder_path = uwp_local_folder_dir()?;
83 Ok(self)
84 }
85
86 pub fn with_filename(mut self, name: &str) -> Self {
88 self.file_name = name.to_string();
89 self
90 }
91
92 pub fn disable_pretty_json(mut self) -> Self {
94 self.json_format = JsonFormat::Compact;
95 self
96 }
97
98 pub fn folder_path(&self) -> &Path {
100 &self.folder_path
101 }
102
103 pub fn path(&self) -> PathBuf {
105 self.folder_path.join(&self.file_name)
106 }
107
108 pub fn save(&self, config: &T) -> Result<()> {
110 if !self.folder_path.exists() {
111 fs::create_dir_all(&self.folder_path)?;
112 }
113
114 let content = match self.json_format {
115 JsonFormat::Compact => serde_json::to_string(config)?,
116 JsonFormat::Pretty => serde_json::to_string_pretty(config)?,
117 };
118
119 fs::write(self.path(), content)?;
120 Ok(())
121 }
122
123 pub fn load(&self) -> Result<T> {
125 let content = fs::read_to_string(self.path())?;
126 Ok(serde_json::from_str(&content)?)
127 }
128}
129
130impl<T> Default for ConfigManager<T>
131where
132 T: Serialize + DeserializeOwned,
133{
134 fn default() -> Self {
135 Self::new()
136 }
137}
138
139impl<T> ConfigManager<T>
140where
141 T: Serialize + DeserializeOwned + Default,
142{
143 pub fn load_or_default(&self) -> Result<T> {
145 let path = self.path();
146
147 match fs::read_to_string(&path) {
148 Ok(content) => Ok(serde_json::from_str(&content)?),
149
150 Err(e) if e.kind() == io::ErrorKind::NotFound => {
151 let default_config = T::default();
152 self.save(&default_config)?;
153 Ok(default_config)
154 }
155
156 Err(e) => Err(ConfigError::Io(e)),
157 }
158 }
159
160 pub fn update<F>(&self, f: F) -> Result<T>
162 where
163 F: FnOnce(&mut T),
164 {
165 let mut cfg = self.load_or_default()?;
166 f(&mut cfg);
167 self.save(&cfg)?;
168 Ok(cfg)
169 }
170}