prehnite_core/settings/
registry.rs

1#![doc = "設定レジストリ"]
2use crate::db::schema::Setting;
3use crate::db::{acquire_or_log, DBType};
4use crate::i18n::{DEFAULT_LANG_ID, SUPPORTED_LANG_ID};
5use crate::opt_unwrap_or_return;
6use crate::settings::value::SettingValueType;
7use crate::settings::{
8    BookSettingKey, GlobalSettingKey, SettingCategory, SettingEntry, SettingKey,
9};
10use sqlx::{Acquire, SqliteConnection};
11use std::collections::HashMap;
12use std::sync::{Arc, LazyLock, RwLock};
13use tracing_unwrap::{OptionExt, ResultExt};
14
15fn fallback_locale() -> String {
16    sys_locale::get_locale()
17        .and_then(|v| {
18            if SUPPORTED_LANG_ID.contains(&v.as_str()) {
19                Some(v)
20            } else {
21                None
22            }
23        })
24        .unwrap_or(DEFAULT_LANG_ID.to_string())
25}
26
27fn registry() -> SettingRegistry {
28    SettingRegistry::default().add_category(
29        SettingCategory::new("settings_category_general")
30            .add(
31                SettingEntry::new(GlobalSettingKey::Locale.into(), fallback_locale().into())
32                    .display_key("settings_entry_locale")
33                    .selectable_values(SUPPORTED_LANG_ID),
34            )
35            .add(
36                SettingEntry::new(GlobalSettingKey::Font.into(), Option::<String>::None.into())
37                    .selectable_values(&[])
38                    .display_key("settings_entry_font"),
39            )
40            .add(
41                SettingEntry::new(
42                    GlobalSettingKey::LastOpened.into(),
43                    Option::<String>::None.into(),
44                )
45                .visibility(false),
46            )
47            .add(
48                SettingEntry::new(GlobalSettingKey::AutoOpenLastOpened.into(), true.into())
49                    .display_key("settings_entry_auto-open-last-opened-file"),
50            ),
51    )
52}
53
54#[derive(Default, Debug)]
55/// 設定レジストリ
56pub struct SettingRegistry {
57    /// 設定カテゴリ
58    pub categories: Vec<SettingCategory>,
59    /// 設定項目
60    pub entries: HashMap<SettingKey, SettingEntry>,
61    /// 実際の設定値
62    values: RwLock<HashMap<SettingKey, SettingValueType>>,
63}
64
65static REGISTRY: LazyLock<Arc<SettingRegistry>> = LazyLock::new(|| Arc::new(registry()));
66
67impl SettingRegistry {
68    fn add_category(mut self, category: SettingCategory) -> Self {
69        category.entries.iter().for_each(|e| {
70            self.values
71                .write()
72                .unwrap_or_log()
73                .insert(e.setting_key.clone(), e.default_value.clone());
74        });
75        self.categories.push(category);
76        if let Some(c) = self.categories.last() {
77            c.entries.iter().for_each(|e| {
78                self.entries.insert(e.setting_key, e.clone());
79            });
80        }
81        self
82    }
83
84    #[tracing::instrument]
85    /// 設定値を取得します。
86    ///
87    /// # Panics
88    /// LockPoisoningが発生している場合、ログに出力し、Panicを起こします。
89    pub fn get(key: &SettingKey) -> Option<SettingValueType> {
90        Some(REGISTRY.values.read().unwrap_or_log().get(key)?.clone())
91    }
92
93    #[tracing::instrument]
94    /// すべての設定値を読み込みます。
95    pub async fn load(target: DBType) -> bool {
96        let values = opt_unwrap_or_return!(
97            async {
98                let mut values: HashMap<SettingKey, SettingValueType> = HashMap::new();
99                let mut conn = match acquire_or_log(target).await {
100                    None => {
101                        return None;
102                    }
103                    Some(conn) => conn,
104                };
105                match Setting::select_all(&mut *conn).await.ok_or_log() {
106                    Some(v) => {
107                        for x in v.into_iter() {
108                            let raw_key = x.setting_key;
109                            if let Ok(key) = match target {
110                                DBType::AppGlobal => {
111                                    GlobalSettingKey::try_from(raw_key.as_str()).map(|v| v.into())
112                                }
113                                DBType::PrehniteBook => {
114                                    BookSettingKey::try_from(raw_key.as_str()).map(|v| v.into())
115                                }
116                            } {
117                                values.insert(
118                                    key,
119                                    match REGISTRY.entries.get(&key) {
120                                        None => SettingValueType::String(x.setting_value),
121                                        Some(v) => v.default_value.converter(x.setting_value),
122                                    },
123                                );
124                            }
125                        }
126                        Some(values)
127                    }
128                    None => None,
129                }
130            }
131            .await,
132            false
133        );
134        match REGISTRY.values.write().ok_or_log() {
135            Some(mut v) => {
136                v.extend(values);
137                true
138            }
139            None => false,
140        }
141    }
142
143    #[tracing::instrument]
144    /// すべての設定値を保存します。
145    pub async fn save(target: DBType) -> bool {
146        let mut conn = match acquire_or_log(target).await {
147            None => return false,
148            Some(conn) => conn,
149        };
150        let mut tx = match conn.begin().await.ok_or_log() {
151            Some(v) => v,
152            None => return false,
153        };
154        for entry in REGISTRY
155            .entries
156            .values()
157            .filter(|v| match v.setting_key {
158                SettingKey::Global(_) => target == DBType::AppGlobal,
159                SettingKey::Book(_) => target == DBType::PrehniteBook,
160            })
161            .into_iter()
162        {
163            // 保存
164            if Self::save_by_key_with_conn(&mut *tx, entry.setting_key)
165                .await
166                .is_err()
167            {
168                tx.rollback().await.ok_or_log();
169                return false;
170            }
171        }
172        tx.commit().await.ok_or_log() != None
173    }
174
175    #[tracing::instrument]
176    /// 特定の設定項目の値を保存します。
177    pub async fn save_by_key(key: SettingKey) -> sqlx::Result<()> {
178        Self::save_by_key_with_conn(
179            &mut *match key.get_conn().await {
180                None => return Ok(()),
181                Some(v) => v,
182            },
183            key,
184        )
185        .await
186    }
187
188    pub(crate) async fn save_by_key_with_conn(
189        conn: &mut SqliteConnection,
190        key: SettingKey,
191    ) -> sqlx::Result<()> {
192        let v = REGISTRY
193            .values
194            .read()
195            .unwrap_or_log()
196            .get(&key)
197            .expect_or_log(&format!("setting value missing!! Key: `{}`", key))
198            .clone();
199        v.save(&mut *conn, key).await
200    }
201
202    #[tracing::instrument]
203    /// 設定項目の初期値を取得します。
204    pub fn get_default(key: SettingKey) -> Option<SettingValueType> {
205        REGISTRY.entries.get(&key).map(|v| v.default_value.clone())
206    }
207
208    /// 設定項目を変更し、値を保存します。
209    pub async fn immediate_apply(key: SettingKey, value: SettingValueType) -> sqlx::Result<()> {
210        match REGISTRY.values.write().unwrap_or_log().get_mut(&key) {
211            None => {}
212            Some(v) => *v = value,
213        };
214        Self::save_by_key(key).await
215    }
216
217    pub(crate) async fn immediate_apply_with_conn(
218        conn: &mut SqliteConnection,
219        key: SettingKey,
220        value: SettingValueType,
221    ) -> sqlx::Result<()> {
222        match REGISTRY.values.write().unwrap_or_log().get_mut(&key) {
223            None => {}
224            Some(v) => *v = value,
225        };
226        Self::save_by_key_with_conn(conn, key).await
227    }
228
229    /// 設定項目の初期値リストを取得します。
230    pub fn get_values() -> HashMap<SettingKey, SettingValueType> {
231        REGISTRY.values.read().unwrap().clone()
232    }
233
234    /// 設定カテゴリを取得します。
235    pub fn get_categories() -> &'static Vec<SettingCategory> {
236        &REGISTRY.categories
237    }
238
239    /// 設定項目を取得します。
240    pub fn get_entries() -> &'static HashMap<SettingKey, SettingEntry> {
241        &REGISTRY.entries
242    }
243}