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