1use core::fmt;
6use std::{any::TypeId, cmp::Ordering, mem, ops, sync::Arc};
7
8use zng_app_context::app_local;
9use zng_state_map::{OwnedStateMap, StateId, StateMapMut, StateMapRef, StateValue};
10use zng_txt::Txt;
11use zng_var::{AnyVar, AnyVarHookArgs, AnyVarValue, BoxedAnyVar, BoxedVar, IntoVar, LocalVar, Var, impl_from_and_into_var, var};
12
13use crate::{CONFIG, Config, ConfigKey, ConfigValue, FallbackConfigReset};
14
15pub struct SETTINGS;
17
18impl SETTINGS {
19 pub fn register(&self, f: impl Fn(&mut SettingsBuilder) + Send + Sync + 'static) {
21 SETTINGS_SV.write().sources.push(Box::new(f))
22 }
23
24 pub fn register_categories(&self, f: impl Fn(&mut CategoriesBuilder) + Send + Sync + 'static) {
26 SETTINGS_SV.write().sources_cat.push(Box::new(f))
27 }
28
29 pub fn get(&self, mut filter: impl FnMut(&ConfigKey, &CategoryId) -> bool, sort: bool) -> Vec<(Category, Vec<Setting>)> {
31 self.get_impl(&mut filter, sort)
32 }
33
34 fn get_impl(&self, filter: &mut dyn FnMut(&ConfigKey, &CategoryId) -> bool, sort: bool) -> Vec<(Category, Vec<Setting>)> {
35 let sv = SETTINGS_SV.read();
36
37 let mut settings = SettingsBuilder { settings: vec![], filter };
38 for source in sv.sources.iter() {
39 source(&mut settings);
40 }
41 let settings = settings.settings;
42
43 let mut categories = CategoriesBuilder {
44 categories: vec![],
45 filter: &mut |cat| settings.iter().any(|s| &s.category == cat),
46 };
47 for source in sv.sources_cat.iter() {
48 source(&mut categories);
49 }
50 let categories = categories.categories;
51
52 let mut result: Vec<_> = categories.into_iter().map(|c| (c, vec![])).collect();
53 for s in settings {
54 if let Some(i) = result.iter().position(|(c, _)| c.id == s.category) {
55 result[i].1.push(s);
56 } else {
57 tracing::warn!("missing category metadata for {}", s.category);
58 result.push((
59 Category {
60 id: s.category.clone(),
61 order: u16::MAX,
62 name: LocalVar(s.category.0.clone()).boxed(),
63 meta: Arc::new(OwnedStateMap::new()),
64 },
65 vec![s],
66 ));
67 }
68 }
69
70 if sort {
71 self.sort(&mut result);
72 }
73 result
74 }
75
76 pub fn any(&self, mut filter: impl FnMut(&ConfigKey, &CategoryId) -> bool) -> bool {
78 self.any_impl(&mut filter)
79 }
80 fn any_impl(&self, filter: &mut dyn FnMut(&ConfigKey, &CategoryId) -> bool) -> bool {
81 let sv = SETTINGS_SV.read();
82
83 let mut any = false;
84
85 for source in sv.sources.iter() {
86 source(&mut SettingsBuilder {
87 settings: vec![],
88 filter: &mut |k, i| {
89 if filter(k, i) {
90 any = true;
91 }
92 false
93 },
94 });
95 if any {
96 break;
97 }
98 }
99
100 any
101 }
102
103 pub fn count(&self, mut filter: impl FnMut(&ConfigKey, &CategoryId) -> bool) -> usize {
105 self.count_impl(&mut filter)
106 }
107 fn count_impl(&self, filter: &mut dyn FnMut(&ConfigKey, &CategoryId) -> bool) -> usize {
108 let sv = SETTINGS_SV.read();
109
110 let mut count = 0;
111
112 for source in sv.sources.iter() {
113 source(&mut SettingsBuilder {
114 settings: vec![],
115 filter: &mut |k, i| {
116 if filter(k, i) {
117 count += 1;
118 }
119 false
120 },
121 });
122 }
123
124 count
125 }
126
127 pub fn categories(&self, mut filter: impl FnMut(&CategoryId) -> bool, include_empty: bool, sort: bool) -> Vec<Category> {
131 self.categories_impl(&mut filter, include_empty, sort)
132 }
133 fn categories_impl(&self, filter: &mut dyn FnMut(&CategoryId) -> bool, include_empty: bool, sort: bool) -> Vec<Category> {
134 let sv = SETTINGS_SV.read();
135
136 let mut categories = CategoriesBuilder {
137 categories: vec![],
138 filter,
139 };
140 for source in sv.sources_cat.iter() {
141 source(&mut categories);
142 }
143 let mut result = categories.categories;
144
145 if !include_empty {
146 let mut non_empty = vec![];
147 for source in sv.sources.iter() {
148 source(&mut SettingsBuilder {
149 settings: vec![],
150 filter: &mut |_, cat| {
151 if !non_empty.contains(cat) {
152 non_empty.push(cat.clone());
153 }
154 false
155 },
156 });
157 }
158
159 result.retain(|c| {
160 if let Some(i) = non_empty.iter().position(|id| &c.id == id) {
161 non_empty.swap_remove(i);
162 true
163 } else {
164 false
165 }
166 });
167
168 for missing in non_empty {
169 tracing::warn!("missing category metadata for {}", missing);
170 result.push(Category::unknown(missing));
171 }
172 }
173
174 if sort {
175 self.sort_categories(&mut result)
176 }
177
178 result
179 }
180
181 pub fn sort_settings(&self, settings: &mut [Setting]) {
183 settings.sort_by(|a, b| {
184 let c = a.order.cmp(&b.order);
185 if matches!(c, Ordering::Equal) {
186 return a.name.with(|a| b.name.with(|b| a.cmp(b)));
187 }
188 c
189 });
190 }
191
192 pub fn sort_categories(&self, categories: &mut [Category]) {
194 categories.sort_by(|a, b| {
195 let c = a.order.cmp(&b.order);
196 if matches!(c, Ordering::Equal) {
197 return a.name.with(|a| b.name.with(|b| a.cmp(b)));
198 }
199 c
200 });
201 }
202
203 pub fn sort(&self, settings: &mut [(Category, Vec<Setting>)]) {
205 settings.sort_by(|a, b| {
206 let c = a.0.order.cmp(&b.0.order);
207 if matches!(c, Ordering::Equal) {
208 return a.0.name.with(|a| b.0.name.with(|b| a.cmp(b)));
209 }
210 c
211 });
212 for (_, s) in settings {
213 self.sort_settings(s);
214 }
215 }
216}
217
218#[derive(PartialEq, Eq, Clone, Debug, Hash, Default, serde::Serialize, serde::Deserialize)]
220#[serde(transparent)]
221pub struct CategoryId(pub Txt);
222impl_from_and_into_var! {
223 fn from(id: Txt) -> CategoryId {
224 CategoryId(id)
225 }
226 fn from(id: String) -> CategoryId {
227 CategoryId(id.into())
228 }
229 fn from(id: &'static str) -> CategoryId {
230 CategoryId(id.into())
231 }
232}
233impl ops::Deref for CategoryId {
234 type Target = Txt;
235
236 fn deref(&self) -> &Self::Target {
237 &self.0
238 }
239}
240impl fmt::Display for CategoryId {
241 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
242 fmt::Display::fmt(&self.0, f)
243 }
244}
245
246#[derive(Clone)]
248pub struct Category {
249 id: CategoryId,
250 order: u16,
251 name: BoxedVar<Txt>,
252 meta: Arc<OwnedStateMap<Category>>,
253}
254impl Category {
255 pub fn id(&self) -> &CategoryId {
257 &self.id
258 }
259
260 pub fn order(&self) -> u16 {
264 self.order
265 }
266
267 pub fn name(&self) -> &BoxedVar<Txt> {
269 &self.name
270 }
271
272 pub fn meta(&self) -> StateMapRef<Category> {
274 self.meta.borrow()
275 }
276
277 pub fn unknown(missing: CategoryId) -> Self {
279 Self {
280 id: missing.clone(),
281 order: u16::MAX,
282 name: LocalVar(missing.0).boxed(),
283 meta: Arc::default(),
284 }
285 }
286}
287impl PartialEq for Category {
288 fn eq(&self, other: &Self) -> bool {
289 self.id == other.id
290 }
291}
292impl Eq for Category {}
293impl fmt::Debug for Category {
294 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
295 f.debug_struct("Category").field("id", &self.id).finish_non_exhaustive()
296 }
297}
298
299#[cfg(test)]
300fn _setting_in_var(s: Setting) {
301 let _x = LocalVar(s).get();
302}
303
304pub struct Setting {
306 key: ConfigKey,
307 order: u16,
308 name: BoxedVar<Txt>,
309 description: BoxedVar<Txt>,
310 category: CategoryId,
311 meta: Arc<OwnedStateMap<Setting>>,
312 value: BoxedAnyVar,
313 value_type: TypeId,
314 reset: Arc<dyn SettingReset>,
315}
316impl Clone for Setting {
317 fn clone(&self) -> Self {
318 Self {
319 key: self.key.clone(),
320 order: self.order,
321 name: self.name.clone(),
322 description: self.description.clone(),
323 category: self.category.clone(),
324 meta: self.meta.clone(),
325 value: self.value.clone(),
326 value_type: self.value_type,
327 reset: self.reset.clone(),
328 }
329 }
330}
331impl Setting {
332 pub fn key(&self) -> &ConfigKey {
334 &self.key
335 }
336
337 pub fn order(&self) -> u16 {
341 self.order
342 }
343
344 pub fn name(&self) -> &BoxedVar<Txt> {
346 &self.name
347 }
348 pub fn description(&self) -> &BoxedVar<Txt> {
350 &self.description
351 }
352 pub fn category(&self) -> &CategoryId {
354 &self.category
355 }
356
357 pub fn meta(&self) -> StateMapRef<Setting> {
359 self.meta.borrow()
360 }
361
362 pub fn value_is_set(&self) -> bool {
367 self.value_type != TypeId::of::<SettingValueNotSet>()
368 }
369
370 pub fn value(&self) -> &BoxedAnyVar {
372 &self.value
373 }
374
375 pub fn value_type(&self) -> TypeId {
377 self.value_type
378 }
379
380 pub fn value_downcast<T: ConfigValue>(&self) -> Option<BoxedVar<T>> {
382 if self.value_type == std::any::TypeId::of::<T>() {
383 let v = self.value.clone().double_boxed_any().downcast::<BoxedVar<T>>().unwrap();
384 Some(*v)
385 } else {
386 None
387 }
388 }
389
390 pub fn can_reset(&self) -> BoxedVar<bool> {
392 self.reset.can_reset(&self.key, &self.value)
393 }
394
395 pub fn reset(&self) {
397 self.reset.reset(&self.key, &self.value);
398 }
399
400 pub fn search_index(&self, search: &str) -> Option<usize> {
405 if let Some(key) = search.strip_prefix("@key:") {
406 return if self.key.contains(key) {
407 Some(self.key.len() - search.len())
408 } else {
409 None
410 };
411 }
412
413 let r = self.name.with(|s| {
414 let s = s.to_lowercase();
415 if s.contains(search) { Some(s.len() - search.len()) } else { None }
416 });
417 if r.is_some() {
418 return r;
419 }
420
421 self.description.with(|s| {
422 let s = s.to_lowercase();
423 if s.contains(search) {
424 Some(s.len() - search.len() + usize::MAX / 2)
425 } else {
426 None
427 }
428 })
429 }
430}
431impl PartialEq for Setting {
432 fn eq(&self, other: &Self) -> bool {
433 self.key == other.key
434 }
435}
436impl Eq for Setting {}
437impl fmt::Debug for Setting {
438 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
439 f.debug_struct("Setting").field("key", &self.key).finish_non_exhaustive()
440 }
441}
442
443app_local! {
444 static SETTINGS_SV: SettingsService = SettingsService {
445 sources: vec![],
446 sources_cat: vec![],
447 };
448}
449struct SettingsService {
450 sources: Vec<Box<dyn Fn(&mut SettingsBuilder) + Send + Sync + 'static>>,
451 sources_cat: Vec<Box<dyn Fn(&mut CategoriesBuilder) + Send + Sync + 'static>>,
452}
453
454pub struct SettingsBuilder<'a> {
456 settings: Vec<Setting>,
457 filter: &'a mut dyn FnMut(&ConfigKey, &CategoryId) -> bool,
458}
459impl SettingsBuilder<'_> {
460 pub fn entry(
464 &mut self,
465 config_key: impl Into<ConfigKey>,
466 category_id: impl Into<CategoryId>,
467 builder: impl for<'a, 'b> FnOnce(&'a mut SettingBuilder<'b>) -> &'a mut SettingBuilder<'b>,
468 ) -> &mut Self {
469 if let Some(mut e) = self.entry_impl(config_key.into(), category_id.into()) {
470 builder(&mut e);
471 }
472 self
473 }
474 fn entry_impl(&mut self, config_key: ConfigKey, category_id: CategoryId) -> Option<SettingBuilder> {
475 if (self.filter)(&config_key, &category_id) {
476 if let Some(i) = self.settings.iter().position(|s| s.key == config_key) {
477 let existing = self.settings.swap_remove(i);
478 Some(SettingBuilder {
479 settings: &mut self.settings,
480 config_key,
481 category_id,
482 order: existing.order,
483 name: Some(existing.name),
484 description: Some(existing.description),
485 meta: Arc::try_unwrap(existing.meta).unwrap(),
486 value: None,
487 reset: None,
488 })
489 } else {
490 Some(SettingBuilder {
491 settings: &mut self.settings,
492 config_key,
493 category_id,
494 order: u16::MAX,
495 name: None,
496 description: None,
497 meta: OwnedStateMap::new(),
498 value: None,
499 reset: None,
500 })
501 }
502 } else {
503 None
504 }
505 }
506}
507
508pub struct SettingBuilder<'a> {
510 settings: &'a mut Vec<Setting>,
511 config_key: ConfigKey,
512 category_id: CategoryId,
513 order: u16,
514 name: Option<BoxedVar<Txt>>,
515 description: Option<BoxedVar<Txt>>,
516 meta: OwnedStateMap<Setting>,
517 value: Option<(BoxedAnyVar, TypeId)>,
518 reset: Option<Arc<dyn SettingReset>>,
519}
520impl SettingBuilder<'_> {
521 pub fn key(&self) -> &ConfigKey {
523 &self.config_key
524 }
525 pub fn category(&self) -> &CategoryId {
527 &self.category_id
528 }
529
530 pub fn order(&mut self, order: u16) -> &mut Self {
534 self.order = order;
535 self
536 }
537
538 pub fn name(&mut self, name: impl IntoVar<Txt>) -> &mut Self {
540 self.name = Some(name.into_var().read_only().boxed());
541 self
542 }
543
544 pub fn description(&mut self, description: impl IntoVar<Txt>) -> &mut Self {
546 self.description = Some(description.into_var().read_only().boxed());
547 self
548 }
549
550 pub fn set<T: StateValue>(&mut self, id: impl Into<StateId<T>>, value: impl Into<T>) -> &mut Self {
552 self.meta.borrow_mut().set(id, value);
553 self
554 }
555
556 pub fn flag(&mut self, id: impl Into<StateId<()>>) -> &mut Self {
558 self.meta.borrow_mut().flag(id);
559 self
560 }
561
562 pub fn meta(&mut self) -> StateMapMut<Setting> {
564 self.meta.borrow_mut()
565 }
566
567 pub fn value<T: ConfigValue>(&mut self, default: T) -> &mut Self {
569 self.cfg_value(&mut CONFIG, default)
570 }
571
572 pub fn cfg_value<T: ConfigValue>(&mut self, cfg: &mut impl Config, default: T) -> &mut Self {
574 let value = cfg.get(self.config_key.clone(), default, false);
575 self.value = Some((value.boxed_any(), TypeId::of::<T>()));
576 self
577 }
578
579 pub fn reset(&mut self, resetter: Box<dyn FallbackConfigReset>, strip_key_prefix: impl Into<Txt>) -> &mut Self {
587 self.reset = Some(Arc::new(FallbackReset {
588 resetter,
589 strip_key_prefix: strip_key_prefix.into(),
590 }));
591 self
592 }
593
594 pub fn default<T: ConfigValue>(&mut self, default: T) -> &mut Self {
598 let reset: Box<dyn AnyVarValue> = Box::new(default);
599 self.reset = Some(Arc::new(reset));
600 self
601 }
602}
603impl Drop for SettingBuilder<'_> {
604 fn drop(&mut self) {
605 let (cfg, cfg_type) = self
606 .value
607 .take()
608 .unwrap_or_else(|| (LocalVar(SettingValueNotSet).boxed_any(), TypeId::of::<SettingValueNotSet>()));
609 self.settings.push(Setting {
610 key: mem::take(&mut self.config_key),
611 order: self.order,
612 name: self.name.take().unwrap_or_else(|| var(Txt::from_static("")).boxed()),
613 description: self.description.take().unwrap_or_else(|| var(Txt::from_static("")).boxed()),
614 category: mem::take(&mut self.category_id),
615 meta: Arc::new(mem::take(&mut self.meta)),
616 value: cfg,
617 value_type: cfg_type,
618 reset: self.reset.take().unwrap_or_else(|| Arc::new(SettingValueNotSet)),
619 })
620 }
621}
622
623#[derive(Clone, PartialEq, Debug)]
624struct SettingValueNotSet;
625
626pub struct CategoriesBuilder<'f> {
628 categories: Vec<Category>,
629 filter: &'f mut dyn FnMut(&CategoryId) -> bool,
630}
631impl CategoriesBuilder<'_> {
632 pub fn entry(
636 &mut self,
637 category_id: impl Into<CategoryId>,
638 builder: impl for<'a, 'b> FnOnce(&'a mut CategoryBuilder<'b>) -> &'a mut CategoryBuilder<'b>,
639 ) -> &mut Self {
640 if let Some(mut e) = self.entry_impl(category_id.into()) {
641 builder(&mut e);
642 }
643 self
644 }
645 fn entry_impl(&mut self, category_id: CategoryId) -> Option<CategoryBuilder> {
646 if (self.filter)(&category_id) {
647 if let Some(i) = self.categories.iter().position(|s| s.id == category_id) {
648 let existing = self.categories.swap_remove(i);
649 Some(CategoryBuilder {
650 categories: &mut self.categories,
651 category_id,
652 order: existing.order,
653 name: Some(existing.name),
654 meta: Arc::try_unwrap(existing.meta).unwrap(),
655 })
656 } else {
657 Some(CategoryBuilder {
658 categories: &mut self.categories,
659 category_id,
660 order: u16::MAX,
661 name: None,
662 meta: OwnedStateMap::new(),
663 })
664 }
665 } else {
666 None
667 }
668 }
669}
670
671pub struct CategoryBuilder<'a> {
673 categories: &'a mut Vec<Category>,
674 category_id: CategoryId,
675 order: u16,
676 name: Option<BoxedVar<Txt>>,
677 meta: OwnedStateMap<Category>,
678}
679impl CategoryBuilder<'_> {
680 pub fn id(&self) -> &CategoryId {
682 &self.category_id
683 }
684
685 pub fn order(&mut self, order: u16) -> &mut Self {
689 self.order = order;
690 self
691 }
692
693 pub fn name(&mut self, name: impl IntoVar<Txt>) -> &mut Self {
695 self.name = Some(name.into_var().read_only().boxed());
696 self
697 }
698
699 pub fn set<T: StateValue>(&mut self, id: impl Into<StateId<T>>, value: impl Into<T>) -> &mut Self {
701 self.meta.borrow_mut().set(id, value);
702 self
703 }
704
705 pub fn flag(&mut self, id: impl Into<StateId<()>>) -> &mut Self {
707 self.meta.borrow_mut().flag(id);
708 self
709 }
710
711 pub fn meta(&mut self) -> StateMapMut<Category> {
713 self.meta.borrow_mut()
714 }
715}
716impl Drop for CategoryBuilder<'_> {
717 fn drop(&mut self) {
718 self.categories.push(Category {
719 id: mem::take(&mut self.category_id),
720 order: self.order,
721 name: self.name.take().unwrap_or_else(|| var(Txt::from_static("")).boxed()),
722 meta: Arc::new(mem::take(&mut self.meta)),
723 })
724 }
725}
726trait SettingReset: Send + Sync + 'static {
727 fn can_reset(&self, key: &ConfigKey, value: &BoxedAnyVar) -> BoxedVar<bool>;
728 fn reset(&self, key: &ConfigKey, value: &BoxedAnyVar);
729}
730
731struct FallbackReset {
732 resetter: Box<dyn FallbackConfigReset>,
733 strip_key_prefix: Txt,
734}
735
736impl SettingReset for FallbackReset {
737 fn can_reset(&self, key: &ConfigKey, _: &BoxedAnyVar) -> BoxedVar<bool> {
738 match key.strip_prefix(self.strip_key_prefix.as_str()) {
739 Some(k) => self.resetter.can_reset(ConfigKey::from_str(k)),
740 None => self.resetter.can_reset(key.clone()),
741 }
742 }
743
744 fn reset(&self, key: &ConfigKey, _: &BoxedAnyVar) {
745 match key.strip_prefix(self.strip_key_prefix.as_str()) {
746 Some(k) => self.resetter.reset(&ConfigKey::from_str(k)),
747 None => self.resetter.reset(key),
748 }
749 }
750}
751impl SettingReset for Box<dyn AnyVarValue> {
752 fn can_reset(&self, _: &ConfigKey, value: &BoxedAnyVar) -> BoxedVar<bool> {
753 let mut initial = false;
754 value.with_any(&mut |v| {
755 initial = v.eq_any(&**self);
756 });
757 let map = var(initial);
758
759 let map_in = map.clone();
760 let dft = self.clone_boxed();
761 value
762 .hook_any(Box::new(move |args: &AnyVarHookArgs| {
763 map_in.set(args.value().eq_any(&*dft));
764 true
765 }))
766 .perm();
767
768 map.clone().boxed()
769 }
770
771 fn reset(&self, _: &ConfigKey, value: &BoxedAnyVar) {
772 let _ = value.set_any(self.clone_boxed());
773 }
774}
775impl SettingReset for SettingValueNotSet {
776 fn can_reset(&self, _: &ConfigKey, _: &BoxedAnyVar) -> BoxedVar<bool> {
777 LocalVar(false).boxed()
778 }
779 fn reset(&self, _: &ConfigKey, _: &BoxedAnyVar) {}
780}