1#![doc(html_favicon_url = "https://raw.githubusercontent.com/zng-ui/zng/main/examples/image/res/zng-logo-icon.png")]
2#![doc(html_logo_url = "https://raw.githubusercontent.com/zng-ui/zng/main/examples/image/res/zng-logo.png")]
3#![doc = include_str!(concat!("../", std::env!("CARGO_PKG_README")))]
9#![expect(clippy::type_complexity)]
11#![warn(unused_extern_crates)]
12#![warn(missing_docs)]
13
14mod fallback;
15pub use fallback::*;
16
17mod json;
18pub use json::*;
19
20mod swap;
21pub use swap::*;
22
23mod switch;
24pub use switch::*;
25
26mod sync;
27pub use sync::*;
28
29#[cfg(feature = "toml")]
30mod toml;
31#[cfg(feature = "toml")]
32pub use self::toml::*;
33
34#[cfg(feature = "ron")]
35mod ron;
36#[cfg(feature = "ron")]
37pub use self::ron::*;
38
39#[cfg(feature = "yaml")]
40mod yaml;
41#[cfg(feature = "yaml")]
42pub use self::yaml::*;
43
44pub mod settings;
45
46use std::{
47 any::Any,
48 collections::{HashMap, hash_map},
49 fmt, io,
50 sync::Arc,
51};
52
53use zng_app::{AppExtension, update::EventUpdate, view_process::raw_events::LOW_MEMORY_EVENT};
54use zng_app_context::app_local;
55use zng_clone_move::clmv;
56use zng_ext_fs_watcher::{WatchFile, WatcherReadStatus, WatcherSyncStatus, WriteFile};
57use zng_task as task;
58use zng_txt::Txt;
59use zng_var::{AnyVar, AnyWeakVar, ArcVar, BoxedVar, LocalVar, Var, VarHandles, VarModify, VarValue, WeakVar, types::WeakArcVar, var};
60
61#[derive(Default)]
69#[non_exhaustive]
70pub struct ConfigManager {}
71
72impl AppExtension for ConfigManager {
73 fn event_preview(&mut self, update: &mut EventUpdate) {
74 if LOW_MEMORY_EVENT.on(update).is_some() {
75 CONFIG_SV.write().low_memory();
76 }
77 }
78}
79
80pub struct CONFIG;
87impl CONFIG {
88 pub fn load(&self, source: impl AnyConfig) {
93 CONFIG_SV.write().load(source)
94 }
95
96 pub fn status(&self) -> BoxedVar<ConfigStatus> {
98 CONFIG_SV.read().status()
99 }
100
101 pub async fn wait_idle(&self) {
105 task::yield_now().await; self.status().wait_value(|s| s.is_idle()).await;
107 }
108
109 pub fn get<T: ConfigValue>(&self, key: impl Into<ConfigKey>, default: T) -> BoxedVar<T> {
117 CONFIG_SV.write().get(key.into(), default, false)
118 }
119
120 pub fn insert<T: ConfigValue>(&self, key: impl Into<ConfigKey>, value: T) -> BoxedVar<T> {
122 CONFIG_SV.write().get(key.into(), value, true)
123 }
124}
125impl AnyConfig for CONFIG {
126 fn get_raw(&mut self, key: ConfigKey, default: RawConfigValue, insert: bool, shared: bool) -> BoxedVar<RawConfigValue> {
127 CONFIG_SV.write().get_raw(key, default, insert, shared)
128 }
129
130 fn contains_key(&mut self, key: ConfigKey) -> BoxedVar<bool> {
131 CONFIG_SV.write().contains_key(key)
132 }
133
134 fn status(&self) -> BoxedVar<ConfigStatus> {
135 CONFIG.status()
136 }
137
138 fn remove(&mut self, key: &ConfigKey) -> bool {
139 CONFIG_SV.write().remove(key)
140 }
141
142 fn low_memory(&mut self) {
143 CONFIG_SV.write().low_memory()
144 }
145}
146impl Config for CONFIG {
147 fn get<T: ConfigValue>(&mut self, key: impl Into<ConfigKey>, default: T, insert: bool) -> BoxedVar<T> {
148 CONFIG_SV.write().get(key, default, insert)
149 }
150}
151
152app_local! {
153 static CONFIG_SV: SwapConfig = SwapConfig::new();
154}
155
156pub type ConfigKey = Txt;
158
159#[diagnostic::on_unimplemented(note = "`ConfigValue` is implemented for all `T: VarValue + Serialize + DeserializeOwned`")]
163pub trait ConfigValue: VarValue + serde::Serialize + serde::de::DeserializeOwned {}
164impl<T: VarValue + serde::Serialize + serde::de::DeserializeOwned> ConfigValue for T {}
165
166#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
168pub struct RawConfigValue(pub serde_json::Value);
169impl RawConfigValue {
170 pub fn serialize<T: serde::Serialize>(value: T) -> Result<Self, serde_json::Error> {
172 serde_json::to_value(value).map(Self)
173 }
174
175 pub fn deserialize<T: serde::de::DeserializeOwned>(self) -> Result<T, serde_json::Error> {
177 serde_json::from_value(self.0)
178 }
179}
180
181pub trait ConfigMap: VarValue + fmt::Debug {
185 fn empty() -> Self;
187
188 fn read(file: WatchFile) -> io::Result<Self>;
192 fn write(self, file: &mut WriteFile) -> io::Result<()>;
196
197 fn get_raw(&self, key: &ConfigKey) -> Result<Option<RawConfigValue>, Arc<dyn std::error::Error + Send + Sync>>;
204
205 fn set_raw(map: &mut VarModify<Self>, key: ConfigKey, value: RawConfigValue) -> Result<(), Arc<dyn std::error::Error + Send + Sync>>;
215
216 fn contains_key(&self, key: &ConfigKey) -> bool;
220
221 fn remove(map: &mut VarModify<Self>, key: &ConfigKey);
223
224 fn get<O: ConfigValue>(&self, key: &ConfigKey) -> Result<Option<O>, Arc<dyn std::error::Error + Send + Sync>> {
228 if let Some(value) = self.get_raw(key)? {
229 match RawConfigValue::deserialize(value) {
230 Ok(s) => Ok(Some(s)),
231 Err(e) => Err(Arc::new(e)),
232 }
233 } else {
234 Ok(None)
235 }
236 }
237
238 fn set<O: ConfigValue>(map: &mut VarModify<Self>, key: ConfigKey, value: O) -> Result<(), Arc<dyn std::error::Error + Send + Sync>> {
246 match RawConfigValue::serialize(value) {
247 Ok(s) => Self::set_raw(map, key, s),
248 Err(e) => Err(Arc::new(e)),
249 }
250 }
251}
252
253pub trait AnyConfig: Send + Any {
257 fn status(&self) -> BoxedVar<ConfigStatus>;
259
260 fn get_raw(&mut self, key: ConfigKey, default: RawConfigValue, insert: bool, shared: bool) -> BoxedVar<RawConfigValue>;
272
273 fn contains_key(&mut self, key: ConfigKey) -> BoxedVar<bool>;
275
276 fn remove(&mut self, key: &ConfigKey) -> bool;
284
285 fn low_memory(&mut self);
287}
288impl dyn AnyConfig {
289 pub fn get_raw_serde_bidi<T: ConfigValue>(&mut self, key: impl Into<ConfigKey>, default: T, insert: bool, shared: bool) -> BoxedVar<T> {
293 let key = key.into();
294 let source_var = self.get_raw(
295 key.clone(),
296 RawConfigValue::serialize(&default).unwrap_or_else(|e| panic!("invalid default value, {e}")),
297 insert,
298 shared,
299 );
300 let var = var(RawConfigValue::deserialize(source_var.get()).unwrap_or(default));
301
302 source_var
303 .bind_filter_map_bidi(
304 &var,
305 clmv!(key, |raw| {
307 match RawConfigValue::deserialize(raw.clone()) {
308 Ok(value) => Some(value),
309 Err(e) => {
310 tracing::error!("get_raw_serde_bidi({key:?}) error, {e:?}");
311 None
312 }
313 }
314 }),
315 clmv!(key, source_var, |value| {
317 let _strong_ref = &source_var;
318
319 match RawConfigValue::serialize(value) {
320 Ok(raw) => Some(raw),
321 Err(e) => {
322 tracing::error!("get_raw_serde_bidi({key:?}) error, {e:?}");
323 None
324 }
325 }
326 }),
327 )
328 .perm();
329
330 var.boxed()
331 }
332}
333
334pub trait Config: AnyConfig {
336 fn get<T: ConfigValue>(&mut self, key: impl Into<ConfigKey>, default: T, insert: bool) -> BoxedVar<T>;
344}
345
346pub struct ReadOnlyConfig<C: Config> {
348 cfg: C,
349}
350impl<C: Config> ReadOnlyConfig<C> {
351 pub fn new(cfg: C) -> Self {
353 Self { cfg }
354 }
355}
356impl<C: Config> AnyConfig for ReadOnlyConfig<C> {
357 fn get_raw(&mut self, key: ConfigKey, default: RawConfigValue, _: bool, shared: bool) -> BoxedVar<RawConfigValue> {
358 self.cfg.get_raw(key, default, false, shared).read_only()
359 }
360
361 fn contains_key(&mut self, key: ConfigKey) -> BoxedVar<bool> {
362 self.cfg.contains_key(key)
363 }
364
365 fn status(&self) -> BoxedVar<ConfigStatus> {
366 self.cfg.status()
367 }
368
369 fn remove(&mut self, _key: &ConfigKey) -> bool {
370 false
371 }
372
373 fn low_memory(&mut self) {
374 self.cfg.low_memory()
375 }
376}
377impl<C: Config> Config for ReadOnlyConfig<C> {
378 fn get<T: ConfigValue>(&mut self, key: impl Into<ConfigKey>, default: T, _: bool) -> BoxedVar<T> {
379 self.cfg.get(key.into(), default, false).read_only()
380 }
381}
382
383#[derive(Default)]
387pub struct MemoryConfig {
388 values: HashMap<ConfigKey, ArcVar<RawConfigValue>>,
389 contains: HashMap<ConfigKey, WeakArcVar<bool>>,
390}
391
392impl AnyConfig for MemoryConfig {
393 fn status(&self) -> BoxedVar<ConfigStatus> {
394 LocalVar(ConfigStatus::Loaded).boxed()
395 }
396
397 fn get_raw(&mut self, key: ConfigKey, default: RawConfigValue, _insert: bool, _shared: bool) -> BoxedVar<RawConfigValue> {
398 match self.values.entry(key) {
399 hash_map::Entry::Occupied(e) => e.get().clone().boxed(),
400 hash_map::Entry::Vacant(e) => {
401 let r = var(default);
402
403 if let Some(v) = self.contains.get(e.key()) {
404 if let Some(v) = v.upgrade() {
405 v.set(true);
406 }
407 }
408
409 e.insert(r).clone().boxed()
410 }
411 }
412 }
413
414 fn contains_key(&mut self, key: ConfigKey) -> BoxedVar<bool> {
415 match self.contains.entry(key) {
416 hash_map::Entry::Occupied(mut e) => {
417 if let Some(r) = e.get().upgrade() {
418 r.boxed()
419 } else {
420 let r = var(self.values.contains_key(e.key()));
421 e.insert(r.downgrade());
422 r.boxed()
423 }
424 }
425 hash_map::Entry::Vacant(e) => {
426 let r = var(self.values.contains_key(e.key()));
427 e.insert(r.downgrade());
428 r.boxed()
429 }
430 }
431 }
432
433 fn remove(&mut self, key: &ConfigKey) -> bool {
434 if self.values.remove(key).is_some() {
435 self.contains.retain(|_, v| v.strong_count() > 0);
436
437 if let Some(v) = self.contains.get(key) {
438 if let Some(v) = v.upgrade() {
439 v.set(false);
440 }
441 }
442 true
443 } else {
444 false
445 }
446 }
447
448 fn low_memory(&mut self) {
449 self.contains.retain(|_, v| v.strong_count() > 0);
450 }
451}
452impl Config for MemoryConfig {
453 fn get<T: ConfigValue>(&mut self, key: impl Into<ConfigKey>, default: T, insert: bool) -> BoxedVar<T> {
454 self.get_raw(key.into(), RawConfigValue::serialize(default.clone()).unwrap(), insert, true)
455 .filter_map_bidi(
456 |m| m.clone().deserialize::<T>().ok(),
457 |v| RawConfigValue::serialize(v).ok(),
458 move || default.clone(),
459 )
460 .boxed()
461 }
462}
463
464struct ConfigVar<T: ConfigValue> {
465 var: WeakArcVar<T>,
466 binding: VarHandles,
467}
468impl<T: ConfigValue> ConfigVar<T> {
469 fn new_any(var: WeakArcVar<T>, binding: VarHandles) -> Box<dyn AnyConfigVar> {
470 Box::new(Self { var, binding })
471 }
472}
473struct ConfigContainsVar {
474 var: WeakArcVar<bool>,
475 binding: VarHandles,
476}
477
478#[derive(Default)]
482pub struct ConfigVars {
483 values: HashMap<ConfigKey, Box<dyn AnyConfigVar>>,
484 contains: HashMap<ConfigKey, ConfigContainsVar>,
485}
486impl ConfigVars {
487 pub fn get_or_bind<T: ConfigValue>(&mut self, key: ConfigKey, bind: impl FnOnce(&ConfigKey) -> BoxedVar<T>) -> BoxedVar<T> {
489 match self.values.entry(key) {
490 hash_map::Entry::Occupied(mut e) => {
491 if e.get().can_upgrade() {
492 if let Some(x) = e.get().as_any().downcast_ref::<ConfigVar<T>>() {
493 if let Some(var) = x.var.upgrade() {
494 return var.boxed();
495 }
496 } else {
497 tracing::error!(
498 "cannot get key `{}` as `{}` because it is already requested with a different type",
499 e.key(),
500 std::any::type_name::<T>()
501 );
502 return bind(e.key());
503 }
504 }
505 let cfg = bind(e.key());
507
508 let res = var(cfg.get());
509 let binding = res.bind_map_bidi(
510 &cfg,
511 clmv!(cfg, |v| {
512 let _strong_ref = &cfg;
513 v.clone()
514 }),
515 Clone::clone,
516 );
517
518 e.insert(ConfigVar::new_any(res.downgrade(), binding));
519 res.boxed()
520 }
521 hash_map::Entry::Vacant(e) => {
522 let cfg = bind(e.key());
523 let res = var(cfg.get());
524 let binding = res.bind_map_bidi(
525 &cfg,
526 clmv!(cfg, |v| {
527 let _strong_ref = &cfg;
528 v.clone()
529 }),
530 Clone::clone,
531 );
532
533 e.insert(ConfigVar::new_any(res.downgrade(), binding));
534 res.boxed()
535 }
536 }
537 }
538
539 pub fn get_or_bind_contains(&mut self, key: ConfigKey, bind: impl FnOnce(&ConfigKey) -> BoxedVar<bool>) -> BoxedVar<bool> {
541 match self.contains.entry(key) {
542 hash_map::Entry::Occupied(mut e) => {
543 if let Some(res) = e.get().var.upgrade() {
544 return res.boxed();
545 }
546
547 let cfg = bind(e.key());
548 let res = var(cfg.get());
549
550 let binding = VarHandles(vec![
551 cfg.bind(&res),
552 res.hook_any(Box::new(move |_| {
553 let _strong_ref = &cfg;
554 true
555 })),
556 ]);
557
558 e.insert(ConfigContainsVar {
559 var: res.downgrade(),
560 binding,
561 });
562
563 res.boxed()
564 }
565 hash_map::Entry::Vacant(e) => {
566 let cfg = bind(e.key());
567 let res = var(cfg.get());
568
569 let binding = VarHandles(vec![
570 cfg.bind(&res),
571 res.hook_any(Box::new(move |_| {
572 let _strong_ref = &cfg;
573 true
574 })),
575 ]);
576
577 e.insert(ConfigContainsVar {
578 var: res.downgrade(),
579 binding,
580 });
581
582 res.boxed()
583 }
584 }
585 }
586
587 pub fn rebind(&mut self, source: &mut dyn AnyConfig) {
592 self.values.retain(|key, wk_var| wk_var.rebind(key, source));
593 self.contains.retain(|key, wk_var| wk_var.rebind(key, source));
594 }
595
596 pub fn low_memory(&mut self) {
598 self.values.retain(|_, v| v.can_upgrade());
599 self.contains.retain(|_, v| v.var.strong_count() > 0)
600 }
601}
602trait AnyConfigVar: Any + Send + Sync {
603 fn as_any(&self) -> &dyn Any;
604 fn can_upgrade(&self) -> bool;
605 fn rebind(&mut self, key: &ConfigKey, source: &mut dyn AnyConfig) -> bool;
606}
607impl<T: ConfigValue> AnyConfigVar for ConfigVar<T> {
608 fn as_any(&self) -> &dyn Any {
609 self
610 }
611
612 fn can_upgrade(&self) -> bool {
613 self.var.strong_count() > 0
614 }
615
616 fn rebind(&mut self, key: &ConfigKey, source: &mut dyn AnyConfig) -> bool {
617 let var = if let Some(var) = self.var.upgrade() {
618 var
619 } else {
620 return false;
622 };
623
624 let source_var = source.get_raw(key.clone(), RawConfigValue::serialize(var.get()).unwrap(), false, false);
626
627 var.modify(clmv!(source_var, key, |vm| {
629 match RawConfigValue::deserialize::<T>(source_var.get()) {
630 Ok(value) => {
631 vm.set(value);
632 }
633 Err(e) => {
634 tracing::error!("rebind config get({key:?}) error, {e:?}");
636
637 let _ = source_var.set(RawConfigValue::serialize(vm.as_ref()).unwrap());
639 }
640 }
641 }));
642
643 let mut first = true;
644 self.binding = source_var.bind_filter_map_bidi(
645 &var,
646 clmv!(key, |raw| {
648 match RawConfigValue::deserialize(raw.clone()) {
649 Ok(value) => Some(value),
650 Err(e) => {
651 tracing::error!("rebind config get({key:?}) error, {e:?}");
652 None
653 }
654 }
655 }),
656 clmv!(key, source_var, |value| {
658 if std::mem::take(&mut first) {
659 return None; }
661
662 let _strong_ref = &source_var;
663 match RawConfigValue::serialize(value) {
664 Ok(raw) => Some(raw),
665 Err(e) => {
666 tracing::error!("rebind config set({key:?}) error, {e:?}");
667 None
668 }
669 }
670 }),
671 );
672
673 true
674 }
675}
676impl ConfigContainsVar {
677 fn rebind(&mut self, key: &ConfigKey, source: &mut dyn AnyConfig) -> bool {
678 if let Some(res) = self.var.upgrade() {
679 let cfg = source.contains_key(key.clone());
680 res.set_from(&cfg);
681
682 self.binding = VarHandles(vec![
683 cfg.bind(&res),
684 res.hook_any(Box::new(move |_| {
685 let _strong_ref = &cfg;
686 true
687 })),
688 ]);
689
690 true
691 } else {
692 false
693 }
694 }
695}
696
697#[derive(Debug, Clone)]
699pub enum ConfigStatus {
700 Loaded,
702 Loading,
704 Saving,
706 LoadErrors(ConfigStatusError),
708 SaveErrors(ConfigStatusError),
710}
711impl ConfigStatus {
712 pub fn is_idle(&self) -> bool {
714 !matches!(self, Self::Loading | Self::Saving)
715 }
716
717 pub fn is_err(&self) -> bool {
719 matches!(self, ConfigStatus::LoadErrors(_) | ConfigStatus::SaveErrors(_))
720 }
721
722 pub fn errors(&self) -> &[Arc<dyn std::error::Error + Send + Sync>] {
728 match self {
729 ConfigStatus::LoadErrors(e) => e,
730 ConfigStatus::SaveErrors(e) => e,
731 _ => &[],
732 }
733 }
734
735 pub fn merge_status(status: impl Iterator<Item = ConfigStatus>) -> ConfigStatus {
737 let mut load_errors = vec![];
738 let mut save_errors = vec![];
739 let mut loading = false;
740 let mut saving = false;
741 for s in status {
742 match s {
743 ConfigStatus::Loaded => {}
744 ConfigStatus::Loading => loading = true,
745 ConfigStatus::Saving => saving = true,
746 ConfigStatus::LoadErrors(e) => {
747 if load_errors.is_empty() {
748 load_errors = e;
749 } else {
750 load_errors.extend(e);
751 }
752 }
753 ConfigStatus::SaveErrors(e) => {
754 if save_errors.is_empty() {
755 save_errors = e;
756 } else {
757 save_errors.extend(e);
758 }
759 }
760 }
761 }
762
763 if loading {
764 ConfigStatus::Loading
765 } else if saving {
766 ConfigStatus::Saving
767 } else if !load_errors.is_empty() {
768 ConfigStatus::LoadErrors(load_errors)
769 } else if !save_errors.is_empty() {
770 ConfigStatus::SaveErrors(save_errors)
771 } else {
772 ConfigStatus::Loaded
773 }
774 }
775}
776impl fmt::Display for ConfigStatus {
777 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
778 match self {
779 Self::Loaded => Ok(()),
780 Self::Loading => write!(f, "loading…"),
781 Self::Saving => write!(f, "saving…"),
782 Self::LoadErrors(e) => {
783 writeln!(f, "read errors:")?;
784 for e in e {
785 writeln!(f, " {e}")?;
786 }
787 Ok(())
788 }
789 Self::SaveErrors(e) => {
790 writeln!(f, "write errors:")?;
791 for e in e {
792 writeln!(f, " {e}")?;
793 }
794 Ok(())
795 }
796 }
797 }
798}
799impl PartialEq for ConfigStatus {
800 fn eq(&self, other: &Self) -> bool {
801 match (self, other) {
802 (Self::LoadErrors(a), Self::LoadErrors(b)) => a.is_empty() && b.is_empty(),
803 (Self::SaveErrors(a), Self::SaveErrors(b)) => a.is_empty() && b.is_empty(),
804 _ => core::mem::discriminant(self) == core::mem::discriminant(other),
805 }
806 }
807}
808impl Eq for ConfigStatus {}
809impl WatcherSyncStatus<ConfigStatusError, ConfigStatusError> for ConfigStatus {
810 fn writing() -> Self {
811 ConfigStatus::Saving
812 }
813
814 fn write_error(e: ConfigStatusError) -> Self {
815 ConfigStatus::SaveErrors(e)
816 }
817}
818impl WatcherReadStatus<ConfigStatusError> for ConfigStatus {
819 fn idle() -> Self {
820 ConfigStatus::Loaded
821 }
822
823 fn reading() -> Self {
824 ConfigStatus::Loading
825 }
826
827 fn read_error(e: ConfigStatusError) -> Self {
828 ConfigStatus::LoadErrors(e)
829 }
830}
831type ConfigStatusError = Vec<Arc<dyn std::error::Error + Send + Sync>>;