prehnite_core/settings/
registry.rs1#![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)]
55pub struct SettingRegistry {
57 pub categories: Vec<SettingCategory>,
59 pub entries: HashMap<SettingKey, SettingEntry>,
61 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 pub fn get(key: &SettingKey) -> Option<SettingValueType> {
90 Some(REGISTRY.values.read().unwrap_or_log().get(key)?.clone())
91 }
92
93 #[tracing::instrument]
94 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 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 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 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 pub fn get_default(key: SettingKey) -> Option<SettingValueType> {
205 REGISTRY.entries.get(&key).map(|v| v.default_value.clone())
206 }
207
208 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 pub fn get_values() -> HashMap<SettingKey, SettingValueType> {
231 REGISTRY.values.read().unwrap().clone()
232 }
233
234 pub fn get_categories() -> &'static Vec<SettingCategory> {
236 ®ISTRY.categories
237 }
238
239 pub fn get_entries() -> &'static HashMap<SettingKey, SettingEntry> {
241 ®ISTRY.entries
242 }
243}