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)]
69pub struct ConfigManager {}
70
71impl AppExtension for ConfigManager {
72 fn event_preview(&mut self, update: &mut EventUpdate) {
73 if LOW_MEMORY_EVENT.on(update).is_some() {
74 CONFIG_SV.write().low_memory();
75 }
76 }
77}
78
79pub struct CONFIG;
86impl CONFIG {
87 pub fn load(&self, source: impl AnyConfig) {
92 CONFIG_SV.write().load(source)
93 }
94
95 pub fn status(&self) -> BoxedVar<ConfigStatus> {
97 CONFIG_SV.read().status()
98 }
99
100 pub async fn wait_idle(&self) {
104 task::yield_now().await; self.status().wait_value(|s| s.is_idle()).await;
106 }
107
108 pub fn get<T: ConfigValue>(&self, key: impl Into<ConfigKey>, default: T) -> BoxedVar<T> {
116 CONFIG_SV.write().get(key.into(), default, false)
117 }
118
119 pub fn insert<T: ConfigValue>(&self, key: impl Into<ConfigKey>, value: T) -> BoxedVar<T> {
121 CONFIG_SV.write().get(key.into(), value, true)
122 }
123}
124impl AnyConfig for CONFIG {
125 fn get_raw(&mut self, key: ConfigKey, default: RawConfigValue, insert: bool, shared: bool) -> BoxedVar<RawConfigValue> {
126 CONFIG_SV.write().get_raw(key, default, insert, shared)
127 }
128
129 fn contains_key(&mut self, key: ConfigKey) -> BoxedVar<bool> {
130 CONFIG_SV.write().contains_key(key)
131 }
132
133 fn status(&self) -> BoxedVar<ConfigStatus> {
134 CONFIG.status()
135 }
136
137 fn remove(&mut self, key: &ConfigKey) -> bool {
138 CONFIG_SV.write().remove(key)
139 }
140
141 fn low_memory(&mut self) {
142 CONFIG_SV.write().low_memory()
143 }
144}
145impl Config for CONFIG {
146 fn get<T: ConfigValue>(&mut self, key: impl Into<ConfigKey>, default: T, insert: bool) -> BoxedVar<T> {
147 CONFIG_SV.write().get(key, default, insert)
148 }
149}
150
151app_local! {
152 static CONFIG_SV: SwapConfig = SwapConfig::new();
153}
154
155pub type ConfigKey = Txt;
157
158#[diagnostic::on_unimplemented(note = "`ConfigValue` is implemented for all `T: VarValue + Serialize + DeserializeOwned`")]
162pub trait ConfigValue: VarValue + serde::Serialize + serde::de::DeserializeOwned {}
163impl<T: VarValue + serde::Serialize + serde::de::DeserializeOwned> ConfigValue for T {}
164
165#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
167pub struct RawConfigValue(pub serde_json::Value);
168impl RawConfigValue {
169 pub fn serialize<T: serde::Serialize>(value: T) -> Result<Self, serde_json::Error> {
171 serde_json::to_value(value).map(Self)
172 }
173
174 pub fn deserialize<T: serde::de::DeserializeOwned>(self) -> Result<T, serde_json::Error> {
176 serde_json::from_value(self.0)
177 }
178}
179
180pub trait ConfigMap: VarValue + fmt::Debug {
184 fn empty() -> Self;
186
187 fn read(file: WatchFile) -> io::Result<Self>;
191 fn write(self, file: &mut WriteFile) -> io::Result<()>;
195
196 fn get_raw(&self, key: &ConfigKey) -> Result<Option<RawConfigValue>, Arc<dyn std::error::Error + Send + Sync>>;
203
204 fn set_raw(map: &mut VarModify<Self>, key: ConfigKey, value: RawConfigValue) -> Result<(), Arc<dyn std::error::Error + Send + Sync>>;
214
215 fn contains_key(&self, key: &ConfigKey) -> bool;
219
220 fn remove(map: &mut VarModify<Self>, key: &ConfigKey);
222
223 fn get<O: ConfigValue>(&self, key: &ConfigKey) -> Result<Option<O>, Arc<dyn std::error::Error + Send + Sync>> {
227 if let Some(value) = self.get_raw(key)? {
228 match RawConfigValue::deserialize(value) {
229 Ok(s) => Ok(Some(s)),
230 Err(e) => Err(Arc::new(e)),
231 }
232 } else {
233 Ok(None)
234 }
235 }
236
237 fn set<O: ConfigValue>(map: &mut VarModify<Self>, key: ConfigKey, value: O) -> Result<(), Arc<dyn std::error::Error + Send + Sync>> {
245 match RawConfigValue::serialize(value) {
246 Ok(s) => Self::set_raw(map, key, s),
247 Err(e) => Err(Arc::new(e)),
248 }
249 }
250}
251
252pub trait AnyConfig: Send + Any {
256 fn status(&self) -> BoxedVar<ConfigStatus>;
258
259 fn get_raw(&mut self, key: ConfigKey, default: RawConfigValue, insert: bool, shared: bool) -> BoxedVar<RawConfigValue>;
271
272 fn contains_key(&mut self, key: ConfigKey) -> BoxedVar<bool>;
274
275 fn remove(&mut self, key: &ConfigKey) -> bool;
283
284 fn low_memory(&mut self);
286}
287impl dyn AnyConfig {
288 pub fn get_raw_serde_bidi<T: ConfigValue>(&mut self, key: impl Into<ConfigKey>, default: T, insert: bool, shared: bool) -> BoxedVar<T> {
292 let key = key.into();
293 let source_var = self.get_raw(
294 key.clone(),
295 RawConfigValue::serialize(&default).unwrap_or_else(|e| panic!("invalid default value, {e}")),
296 insert,
297 shared,
298 );
299 let var = var(RawConfigValue::deserialize(source_var.get()).unwrap_or(default));
300
301 source_var
302 .bind_filter_map_bidi(
303 &var,
304 clmv!(key, |raw| {
306 match RawConfigValue::deserialize(raw.clone()) {
307 Ok(value) => Some(value),
308 Err(e) => {
309 tracing::error!("get_raw_serde_bidi({key:?}) error, {e:?}");
310 None
311 }
312 }
313 }),
314 clmv!(key, source_var, |value| {
316 let _strong_ref = &source_var;
317
318 match RawConfigValue::serialize(value) {
319 Ok(raw) => Some(raw),
320 Err(e) => {
321 tracing::error!("get_raw_serde_bidi({key:?}) error, {e:?}");
322 None
323 }
324 }
325 }),
326 )
327 .perm();
328
329 var.boxed()
330 }
331}
332
333pub trait Config: AnyConfig {
335 fn get<T: ConfigValue>(&mut self, key: impl Into<ConfigKey>, default: T, insert: bool) -> BoxedVar<T>;
343}
344
345pub struct ReadOnlyConfig<C: Config> {
347 cfg: C,
348}
349impl<C: Config> ReadOnlyConfig<C> {
350 pub fn new(cfg: C) -> Self {
352 Self { cfg }
353 }
354}
355impl<C: Config> AnyConfig for ReadOnlyConfig<C> {
356 fn get_raw(&mut self, key: ConfigKey, default: RawConfigValue, _: bool, shared: bool) -> BoxedVar<RawConfigValue> {
357 self.cfg.get_raw(key, default, false, shared).read_only()
358 }
359
360 fn contains_key(&mut self, key: ConfigKey) -> BoxedVar<bool> {
361 self.cfg.contains_key(key)
362 }
363
364 fn status(&self) -> BoxedVar<ConfigStatus> {
365 self.cfg.status()
366 }
367
368 fn remove(&mut self, _key: &ConfigKey) -> bool {
369 false
370 }
371
372 fn low_memory(&mut self) {
373 self.cfg.low_memory()
374 }
375}
376impl<C: Config> Config for ReadOnlyConfig<C> {
377 fn get<T: ConfigValue>(&mut self, key: impl Into<ConfigKey>, default: T, _: bool) -> BoxedVar<T> {
378 self.cfg.get(key.into(), default, false).read_only()
379 }
380}
381
382#[derive(Default)]
386pub struct MemoryConfig {
387 values: HashMap<ConfigKey, ArcVar<RawConfigValue>>,
388 contains: HashMap<ConfigKey, WeakArcVar<bool>>,
389}
390
391impl AnyConfig for MemoryConfig {
392 fn status(&self) -> BoxedVar<ConfigStatus> {
393 LocalVar(ConfigStatus::Loaded).boxed()
394 }
395
396 fn get_raw(&mut self, key: ConfigKey, default: RawConfigValue, _insert: bool, _shared: bool) -> BoxedVar<RawConfigValue> {
397 match self.values.entry(key) {
398 hash_map::Entry::Occupied(e) => e.get().clone().boxed(),
399 hash_map::Entry::Vacant(e) => {
400 let r = var(default);
401
402 if let Some(v) = self.contains.get(e.key()) {
403 if let Some(v) = v.upgrade() {
404 v.set(true);
405 }
406 }
407
408 e.insert(r).clone().boxed()
409 }
410 }
411 }
412
413 fn contains_key(&mut self, key: ConfigKey) -> BoxedVar<bool> {
414 match self.contains.entry(key) {
415 hash_map::Entry::Occupied(mut e) => {
416 if let Some(r) = e.get().upgrade() {
417 r.boxed()
418 } else {
419 let r = var(self.values.contains_key(e.key()));
420 e.insert(r.downgrade());
421 r.boxed()
422 }
423 }
424 hash_map::Entry::Vacant(e) => {
425 let r = var(self.values.contains_key(e.key()));
426 e.insert(r.downgrade());
427 r.boxed()
428 }
429 }
430 }
431
432 fn remove(&mut self, key: &ConfigKey) -> bool {
433 if self.values.remove(key).is_some() {
434 self.contains.retain(|_, v| v.strong_count() > 0);
435
436 if let Some(v) = self.contains.get(key) {
437 if let Some(v) = v.upgrade() {
438 v.set(false);
439 }
440 }
441 true
442 } else {
443 false
444 }
445 }
446
447 fn low_memory(&mut self) {
448 self.contains.retain(|_, v| v.strong_count() > 0);
449 }
450}
451impl Config for MemoryConfig {
452 fn get<T: ConfigValue>(&mut self, key: impl Into<ConfigKey>, default: T, insert: bool) -> BoxedVar<T> {
453 self.get_raw(key.into(), RawConfigValue::serialize(default.clone()).unwrap(), insert, true)
454 .filter_map_bidi(
455 |m| m.clone().deserialize::<T>().ok(),
456 |v| RawConfigValue::serialize(v).ok(),
457 move || default.clone(),
458 )
459 .boxed()
460 }
461}
462
463struct ConfigVar<T: ConfigValue> {
464 var: WeakArcVar<T>,
465 binding: VarHandles,
466}
467impl<T: ConfigValue> ConfigVar<T> {
468 fn new_any(var: WeakArcVar<T>, binding: VarHandles) -> Box<dyn AnyConfigVar> {
469 Box::new(Self { var, binding })
470 }
471}
472struct ConfigContainsVar {
473 var: WeakArcVar<bool>,
474 binding: VarHandles,
475}
476
477#[derive(Default)]
481pub struct ConfigVars {
482 values: HashMap<ConfigKey, Box<dyn AnyConfigVar>>,
483 contains: HashMap<ConfigKey, ConfigContainsVar>,
484}
485impl ConfigVars {
486 pub fn get_or_bind<T: ConfigValue>(&mut self, key: ConfigKey, bind: impl FnOnce(&ConfigKey) -> BoxedVar<T>) -> BoxedVar<T> {
488 match self.values.entry(key) {
489 hash_map::Entry::Occupied(mut e) => {
490 if e.get().can_upgrade() {
491 if let Some(x) = e.get().as_any().downcast_ref::<ConfigVar<T>>() {
492 if let Some(var) = x.var.upgrade() {
493 return var.boxed();
494 }
495 } else {
496 tracing::error!(
497 "cannot get key `{}` as `{}` because it is already requested with a different type",
498 e.key(),
499 std::any::type_name::<T>()
500 );
501 return bind(e.key());
502 }
503 }
504 let cfg = bind(e.key());
506
507 let res = var(cfg.get());
508 let binding = res.bind_map_bidi(
509 &cfg,
510 clmv!(cfg, |v| {
511 let _strong_ref = &cfg;
512 v.clone()
513 }),
514 Clone::clone,
515 );
516
517 e.insert(ConfigVar::new_any(res.downgrade(), binding));
518 res.boxed()
519 }
520 hash_map::Entry::Vacant(e) => {
521 let cfg = bind(e.key());
522 let res = var(cfg.get());
523 let binding = res.bind_map_bidi(
524 &cfg,
525 clmv!(cfg, |v| {
526 let _strong_ref = &cfg;
527 v.clone()
528 }),
529 Clone::clone,
530 );
531
532 e.insert(ConfigVar::new_any(res.downgrade(), binding));
533 res.boxed()
534 }
535 }
536 }
537
538 pub fn get_or_bind_contains(&mut self, key: ConfigKey, bind: impl FnOnce(&ConfigKey) -> BoxedVar<bool>) -> BoxedVar<bool> {
540 match self.contains.entry(key) {
541 hash_map::Entry::Occupied(mut e) => {
542 if let Some(res) = e.get().var.upgrade() {
543 return res.boxed();
544 }
545
546 let cfg = bind(e.key());
547 let res = var(cfg.get());
548
549 let binding = VarHandles(vec![
550 cfg.bind(&res),
551 res.hook_any(Box::new(move |_| {
552 let _strong_ref = &cfg;
553 true
554 })),
555 ]);
556
557 e.insert(ConfigContainsVar {
558 var: res.downgrade(),
559 binding,
560 });
561
562 res.boxed()
563 }
564 hash_map::Entry::Vacant(e) => {
565 let cfg = bind(e.key());
566 let res = var(cfg.get());
567
568 let binding = VarHandles(vec![
569 cfg.bind(&res),
570 res.hook_any(Box::new(move |_| {
571 let _strong_ref = &cfg;
572 true
573 })),
574 ]);
575
576 e.insert(ConfigContainsVar {
577 var: res.downgrade(),
578 binding,
579 });
580
581 res.boxed()
582 }
583 }
584 }
585
586 pub fn rebind(&mut self, source: &mut dyn AnyConfig) {
591 self.values.retain(|key, wk_var| wk_var.rebind(key, source));
592 self.contains.retain(|key, wk_var| wk_var.rebind(key, source));
593 }
594
595 pub fn low_memory(&mut self) {
597 self.values.retain(|_, v| v.can_upgrade());
598 self.contains.retain(|_, v| v.var.strong_count() > 0)
599 }
600}
601trait AnyConfigVar: Any + Send + Sync {
602 fn as_any(&self) -> &dyn Any;
603 fn can_upgrade(&self) -> bool;
604 fn rebind(&mut self, key: &ConfigKey, source: &mut dyn AnyConfig) -> bool;
605}
606impl<T: ConfigValue> AnyConfigVar for ConfigVar<T> {
607 fn as_any(&self) -> &dyn Any {
608 self
609 }
610
611 fn can_upgrade(&self) -> bool {
612 self.var.strong_count() > 0
613 }
614
615 fn rebind(&mut self, key: &ConfigKey, source: &mut dyn AnyConfig) -> bool {
616 let var = if let Some(var) = self.var.upgrade() {
617 var
618 } else {
619 return false;
621 };
622
623 let source_var = source.get_raw(key.clone(), RawConfigValue::serialize(var.get()).unwrap(), false, false);
625
626 var.modify(clmv!(source_var, key, |vm| {
628 match RawConfigValue::deserialize::<T>(source_var.get()) {
629 Ok(value) => {
630 vm.set(value);
631 }
632 Err(e) => {
633 tracing::error!("rebind config get({key:?}) error, {e:?}");
635
636 let _ = source_var.set(RawConfigValue::serialize(vm.as_ref()).unwrap());
638 }
639 }
640 }));
641
642 let mut first = true;
643 self.binding = source_var.bind_filter_map_bidi(
644 &var,
645 clmv!(key, |raw| {
647 match RawConfigValue::deserialize(raw.clone()) {
648 Ok(value) => Some(value),
649 Err(e) => {
650 tracing::error!("rebind config get({key:?}) error, {e:?}");
651 None
652 }
653 }
654 }),
655 clmv!(key, source_var, |value| {
657 if std::mem::take(&mut first) {
658 return None; }
660
661 let _strong_ref = &source_var;
662 match RawConfigValue::serialize(value) {
663 Ok(raw) => Some(raw),
664 Err(e) => {
665 tracing::error!("rebind config set({key:?}) error, {e:?}");
666 None
667 }
668 }
669 }),
670 );
671
672 true
673 }
674}
675impl ConfigContainsVar {
676 fn rebind(&mut self, key: &ConfigKey, source: &mut dyn AnyConfig) -> bool {
677 if let Some(res) = self.var.upgrade() {
678 let cfg = source.contains_key(key.clone());
679 res.set_from(&cfg);
680
681 self.binding = VarHandles(vec![
682 cfg.bind(&res),
683 res.hook_any(Box::new(move |_| {
684 let _strong_ref = &cfg;
685 true
686 })),
687 ]);
688
689 true
690 } else {
691 false
692 }
693 }
694}
695
696#[derive(Debug, Clone)]
698pub enum ConfigStatus {
699 Loaded,
701 Loading,
703 Saving,
705 LoadErrors(ConfigStatusError),
707 SaveErrors(ConfigStatusError),
709}
710impl ConfigStatus {
711 pub fn is_idle(&self) -> bool {
713 !matches!(self, Self::Loading | Self::Saving)
714 }
715
716 pub fn is_err(&self) -> bool {
718 matches!(self, ConfigStatus::LoadErrors(_) | ConfigStatus::SaveErrors(_))
719 }
720
721 pub fn errors(&self) -> &[Arc<dyn std::error::Error + Send + Sync>] {
727 match self {
728 ConfigStatus::LoadErrors(e) => e,
729 ConfigStatus::SaveErrors(e) => e,
730 _ => &[],
731 }
732 }
733
734 pub fn merge_status(status: impl Iterator<Item = ConfigStatus>) -> ConfigStatus {
736 let mut load_errors = vec![];
737 let mut save_errors = vec![];
738 let mut loading = false;
739 let mut saving = false;
740 for s in status {
741 match s {
742 ConfigStatus::Loaded => {}
743 ConfigStatus::Loading => loading = true,
744 ConfigStatus::Saving => saving = true,
745 ConfigStatus::LoadErrors(e) => {
746 if load_errors.is_empty() {
747 load_errors = e;
748 } else {
749 load_errors.extend(e);
750 }
751 }
752 ConfigStatus::SaveErrors(e) => {
753 if save_errors.is_empty() {
754 save_errors = e;
755 } else {
756 save_errors.extend(e);
757 }
758 }
759 }
760 }
761
762 if loading {
763 ConfigStatus::Loading
764 } else if saving {
765 ConfigStatus::Saving
766 } else if !load_errors.is_empty() {
767 ConfigStatus::LoadErrors(load_errors)
768 } else if !save_errors.is_empty() {
769 ConfigStatus::SaveErrors(save_errors)
770 } else {
771 ConfigStatus::Loaded
772 }
773 }
774}
775impl fmt::Display for ConfigStatus {
776 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
777 match self {
778 Self::Loaded => Ok(()),
779 Self::Loading => write!(f, "loading…"),
780 Self::Saving => write!(f, "saving…"),
781 Self::LoadErrors(e) => {
782 writeln!(f, "read errors:")?;
783 for e in e {
784 writeln!(f, " {e}")?;
785 }
786 Ok(())
787 }
788 Self::SaveErrors(e) => {
789 writeln!(f, "write errors:")?;
790 for e in e {
791 writeln!(f, " {e}")?;
792 }
793 Ok(())
794 }
795 }
796 }
797}
798impl PartialEq for ConfigStatus {
799 fn eq(&self, other: &Self) -> bool {
800 match (self, other) {
801 (Self::LoadErrors(a), Self::LoadErrors(b)) => a.is_empty() && b.is_empty(),
802 (Self::SaveErrors(a), Self::SaveErrors(b)) => a.is_empty() && b.is_empty(),
803 _ => core::mem::discriminant(self) == core::mem::discriminant(other),
804 }
805 }
806}
807impl Eq for ConfigStatus {}
808impl WatcherSyncStatus<ConfigStatusError, ConfigStatusError> for ConfigStatus {
809 fn writing() -> Self {
810 ConfigStatus::Saving
811 }
812
813 fn write_error(e: ConfigStatusError) -> Self {
814 ConfigStatus::SaveErrors(e)
815 }
816}
817impl WatcherReadStatus<ConfigStatusError> for ConfigStatus {
818 fn idle() -> Self {
819 ConfigStatus::Loaded
820 }
821
822 fn reading() -> Self {
823 ConfigStatus::Loading
824 }
825
826 fn read_error(e: ConfigStatusError) -> Self {
827 ConfigStatus::LoadErrors(e)
828 }
829}
830type ConfigStatusError = Vec<Arc<dyn std::error::Error + Send + Sync>>;