Skip to main content

app_json_settings/
core.rs

1use serde::{de::DeserializeOwned, Serialize};
2
3use std::fs;
4use std::io;
5use std::marker::PhantomData;
6use std::path::PathBuf;
7
8use crate::core::constant::DEFAULT_FILE_NAME;
9use crate::core::dir::default_config_dir;
10use crate::core::json::JsonFormat;
11use crate::ConfigError;
12use crate::Result;
13
14pub mod constant;
15mod dir;
16pub mod error;
17mod json;
18
19pub struct ConfigManager<T> {
20    folder_path: PathBuf,
21    file_name: String,
22    json_format: JsonFormat,
23    _marker: PhantomData<T>,
24}
25
26impl<T> ConfigManager<T>
27where
28    T: Serialize + DeserializeOwned,
29{
30    /// デフォルト初期化
31    /// フォルダ: OS 標準 config ディレクトリ / app_name
32    pub fn new() -> Self {
33        let app_name = std::env::current_exe()
34            .unwrap()
35            .file_stem()
36            .unwrap()
37            .to_string_lossy()
38            .to_string();
39
40        let folder_path = default_config_dir().join(&app_name);
41
42        Self {
43            folder_path,
44            file_name: DEFAULT_FILE_NAME.to_string(),
45            json_format: JsonFormat::Pretty,
46            _marker: PhantomData,
47        }
48    }
49
50    /// カレントディレクトリに保存
51    pub fn at_current_dir(mut self) -> Self {
52        self.folder_path = std::env::current_dir().unwrap_or_else(|_| PathBuf::from("."));
53        self
54    }
55
56    /// 任意パスに保存
57    pub fn at_custom_dir<P: Into<PathBuf>>(mut self, path: P) -> Self {
58        self.folder_path = path.into();
59        self
60    }
61
62    /// ファイル名変更
63    pub fn with_filename(mut self, name: &str) -> Self {
64        self.file_name = name.to_string();
65        self
66    }
67
68    /// JSON を pretty 形式で保存
69    pub fn disable_pretty_json(mut self) -> Self {
70        self.json_format = JsonFormat::Compact;
71        self
72    }
73
74    /// フルパス取得
75    pub fn path(&self) -> PathBuf {
76        self.folder_path.join(&self.file_name)
77    }
78
79    /// 完全保存(置換書き込み)
80    pub fn save(&self, config: &T) -> Result<()> {
81        if !self.folder_path.exists() {
82            fs::create_dir_all(&self.folder_path)?;
83        }
84
85        let content = match self.json_format {
86            JsonFormat::Compact => serde_json::to_string(config)?,
87            JsonFormat::Pretty => serde_json::to_string_pretty(config)?,
88        };
89
90        fs::write(self.path(), content)?;
91        Ok(())
92    }
93
94    /// ファイルが存在する前提のロード
95    pub fn load(&self) -> Result<T> {
96        let content = fs::read_to_string(self.path())?;
97        Ok(serde_json::from_str(&content)?)
98    }
99}
100
101//
102// Default 対応 API
103//
104
105impl<T> ConfigManager<T>
106where
107    T: Serialize + DeserializeOwned + Default,
108{
109    /// 存在しなければ default を生成して保存
110    pub fn load_or_default(&self) -> Result<T> {
111        let path = self.path();
112
113        match fs::read_to_string(&path) {
114            Ok(content) => Ok(serde_json::from_str(&content)?),
115
116            Err(e) if e.kind() == io::ErrorKind::NotFound => {
117                let default_config = T::default();
118                self.save(&default_config)?;
119                Ok(default_config)
120            }
121
122            Err(e) => Err(ConfigError::Io(e)),
123        }
124    }
125
126    /// 安全な read-modify-write
127    /// save() と並ぶ主要 API
128    pub fn update<F>(&self, f: F) -> Result<T>
129    where
130        F: FnOnce(&mut T),
131    {
132        let mut cfg = self.load_or_default()?;
133        f(&mut cfg);
134        self.save(&cfg)?;
135        Ok(cfg)
136    }
137}