1#![doc(html_favicon_url = "https://zng-ui.github.io/res/zng-logo-icon.png")]
2#![doc(html_logo_url = "https://zng-ui.github.io/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 serde_value;
15
16mod fallback;
17pub use fallback::*;
18
19mod swap;
20pub use swap::*;
21
22mod switch;
23pub use switch::*;
24
25mod sync;
26pub use sync::*;
27
28#[cfg(feature = "json")]
29mod json;
30#[cfg(feature = "json")]
31pub use json::*;
32
33#[cfg(feature = "toml")]
34mod toml;
35#[cfg(feature = "toml")]
36pub use self::toml::*;
37
38#[cfg(feature = "ron")]
39mod ron;
40#[cfg(feature = "ron")]
41pub use self::ron::*;
42
43#[cfg(feature = "yaml")]
44mod yaml;
45#[cfg(feature = "yaml")]
46pub use self::yaml::*;
47
48pub mod settings;
49
50use std::{
51 any::Any,
52 collections::{HashMap, hash_map},
53 fmt, io,
54 sync::Arc,
55};
56
57use zng_app::{APP, AppExtension, update::EventUpdate, view_process::raw_events::LOW_MEMORY_EVENT};
58use zng_app_context::app_local;
59use zng_clone_move::clmv;
60use zng_ext_fs_watcher::{FsWatcherManager, WatchFile, WatcherReadStatus, WatcherSyncStatus, WriteFile};
61use zng_task as task;
62use zng_txt::Txt;
63use zng_var::{Var, VarHandles, VarValue, WeakVar, const_var, var};
64
65#[derive(Default)]
77#[non_exhaustive]
78pub struct ConfigManager {}
79
80impl AppExtension for ConfigManager {
81 fn init(&mut self) {
82 APP.extensions().require::<FsWatcherManager>();
83 }
84 fn event_preview(&mut self, update: &mut EventUpdate) {
85 if LOW_MEMORY_EVENT.on(update).is_some() {
86 CONFIG_SV.write().low_memory();
87 }
88 }
89}
90
91pub struct CONFIG;
102impl CONFIG {
103 pub fn load(&self, source: impl AnyConfig) {
108 CONFIG_SV.write().load(source)
109 }
110
111 pub fn status(&self) -> Var<ConfigStatus> {
113 CONFIG_SV.read().status()
114 }
115
116 pub async fn wait_idle(&self) {
120 task::yield_now().await; self.status().wait_match(|s| s.is_idle()).await;
122 }
123
124 pub fn get<T: ConfigValue>(&self, key: impl Into<ConfigKey>, default: T) -> Var<T> {
132 CONFIG_SV.write().get(key.into(), default, false)
133 }
134
135 pub fn insert<T: ConfigValue>(&self, key: impl Into<ConfigKey>, value: T) -> Var<T> {
138 CONFIG_SV.write().get(key.into(), value, true)
139 }
140}
141impl AnyConfig for CONFIG {
142 fn get_raw(&mut self, key: ConfigKey, default: RawConfigValue, insert: bool) -> Var<RawConfigValue> {
143 CONFIG_SV.write().get_raw(key, default, insert)
144 }
145
146 fn contains_key(&mut self, key: ConfigKey) -> Var<bool> {
147 CONFIG_SV.write().contains_key(key)
148 }
149
150 fn status(&self) -> Var<ConfigStatus> {
151 CONFIG.status()
152 }
153
154 fn remove(&mut self, key: &ConfigKey) -> bool {
155 CONFIG_SV.write().remove(key)
156 }
157
158 fn low_memory(&mut self) {
159 CONFIG_SV.write().low_memory()
160 }
161}
162
163app_local! {
164 static CONFIG_SV: SwapConfig = {
165 APP.extensions().require::<ConfigManager>();
166 SwapConfig::new()
167 };
168}
169
170pub type ConfigKey = Txt;
172
173#[diagnostic::on_unimplemented(note = "`ConfigValue` is implemented for all `T: VarValue + Serialize + DeserializeOwned`")]
177pub trait ConfigValue: VarValue + serde::Serialize + serde::de::DeserializeOwned {}
178impl<T: VarValue + serde::Serialize + serde::de::DeserializeOwned> ConfigValue for T {}
179
180#[repr(transparent)]
182#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
183#[serde(transparent)]
184pub struct RawConfigValue(pub serde_value::Value);
185impl RawConfigValue {
186 pub fn serialize<T: serde::Serialize>(value: T) -> Result<Self, serde_value::SerializerError> {
188 serde_value::to_value(value).map(Self)
189 }
190
191 pub fn deserialize<T: serde::de::DeserializeOwned>(self) -> Result<T, serde_value::DeserializerError> {
193 T::deserialize(self.0)
194 }
195}
196
197pub trait AnyConfig: Send + Any {
201 fn status(&self) -> Var<ConfigStatus>;
203
204 fn get_raw(&mut self, key: ConfigKey, default: RawConfigValue, insert: bool) -> Var<RawConfigValue>;
212
213 fn contains_key(&mut self, key: ConfigKey) -> Var<bool>;
215
216 fn remove(&mut self, key: &ConfigKey) -> bool;
224
225 fn low_memory(&mut self);
227}
228
229pub trait Config: AnyConfig {
233 fn get<T: ConfigValue>(&mut self, key: impl Into<ConfigKey>, default: T, insert: bool) -> Var<T>;
241}
242impl<C: AnyConfig> Config for C {
243 fn get<T: ConfigValue>(&mut self, key: impl Into<ConfigKey>, default: T, insert: bool) -> Var<T> {
244 get_impl(self, insert, key.into(), default)
245 }
246}
247fn get_impl<T: ConfigValue, C: AnyConfig>(source: &mut C, insert: bool, key: ConfigKey, default: T) -> Var<T> {
248 source
249 .get_raw(key, RawConfigValue::serialize(&default).unwrap(), insert)
250 .filter_map_bidi(
251 move |raw| match raw.clone().deserialize() {
252 Ok(v) => Some(v),
253 Err(e) => {
254 #[cfg(debug_assertions)]
255 tracing::error!(
256 "failed to get config as `{}`, raw value was {:?}, {e}",
257 std::any::type_name::<T>(),
258 raw
259 );
260 #[cfg(not(debug_assertions))]
261 tracing::error!("failed to get config, {e}");
262 None
263 }
264 },
265 |v| match RawConfigValue::serialize(v) {
266 Ok(v) => Some(v),
267 Err(e) => {
268 tracing::error!("failed to set config, {e}");
269 None
270 }
271 },
272 move || default.clone(),
273 )
274}
275
276pub struct ReadOnlyConfig<C: Config> {
281 cfg: C,
282}
283impl<C: Config> ReadOnlyConfig<C> {
284 pub fn new(cfg: C) -> Self {
286 Self { cfg }
287 }
288}
289impl<C: Config> AnyConfig for ReadOnlyConfig<C> {
290 fn get_raw(&mut self, key: ConfigKey, default: RawConfigValue, _: bool) -> Var<RawConfigValue> {
291 self.cfg.get_raw(key, default, false).read_only()
292 }
293
294 fn contains_key(&mut self, key: ConfigKey) -> Var<bool> {
295 self.cfg.contains_key(key)
296 }
297
298 fn status(&self) -> Var<ConfigStatus> {
299 self.cfg.status()
300 }
301
302 fn remove(&mut self, _key: &ConfigKey) -> bool {
303 false
304 }
305
306 fn low_memory(&mut self) {
307 self.cfg.low_memory()
308 }
309}
310
311#[derive(Default)]
315pub struct MemoryConfig {
316 values: HashMap<ConfigKey, Var<RawConfigValue>>,
317 contains: HashMap<ConfigKey, WeakVar<bool>>,
318}
319
320impl AnyConfig for MemoryConfig {
321 fn status(&self) -> Var<ConfigStatus> {
322 const_var(ConfigStatus::Loaded)
323 }
324
325 fn get_raw(&mut self, key: ConfigKey, default: RawConfigValue, _insert: bool) -> Var<RawConfigValue> {
326 match self.values.entry(key) {
327 hash_map::Entry::Occupied(e) => e.get().clone(),
328 hash_map::Entry::Vacant(e) => {
329 let r = var(default);
330
331 if let Some(v) = self.contains.get(e.key())
332 && let Some(v) = v.upgrade()
333 {
334 v.set(true);
335 }
336
337 e.insert(r).clone()
338 }
339 }
340 }
341
342 fn contains_key(&mut self, key: ConfigKey) -> Var<bool> {
343 match self.contains.entry(key) {
344 hash_map::Entry::Occupied(mut e) => {
345 if let Some(r) = e.get().upgrade() {
346 r
347 } else {
348 let r = var(self.values.contains_key(e.key()));
349 e.insert(r.downgrade());
350 r
351 }
352 }
353 hash_map::Entry::Vacant(e) => {
354 let r = var(self.values.contains_key(e.key()));
355 e.insert(r.downgrade());
356 r
357 }
358 }
359 }
360
361 fn remove(&mut self, key: &ConfigKey) -> bool {
362 if self.values.remove(key).is_some() {
363 self.contains.retain(|_, v| v.strong_count() > 0);
364
365 if let Some(v) = self.contains.get(key)
366 && let Some(v) = v.upgrade()
367 {
368 v.set(false);
369 }
370 true
371 } else {
372 false
373 }
374 }
375
376 fn low_memory(&mut self) {
377 self.contains.retain(|_, v| v.strong_count() > 0);
378 }
379}
380
381struct ConfigVar<T: ConfigValue> {
382 var: WeakVar<T>,
383 binding: VarHandles,
384}
385impl<T: ConfigValue> ConfigVar<T> {
386 fn new_any(var: WeakVar<T>, binding: VarHandles) -> Box<dyn AnyConfigVar> {
387 Box::new(Self { var, binding })
388 }
389}
390struct ConfigContainsVar {
391 var: WeakVar<bool>,
392 binding: VarHandles,
393}
394
395#[derive(Default)]
399pub struct ConfigVars {
400 values: HashMap<ConfigKey, Box<dyn AnyConfigVar>>,
401 contains: HashMap<ConfigKey, ConfigContainsVar>,
402}
403impl ConfigVars {
404 pub fn get_or_bind<T: ConfigValue>(&mut self, key: ConfigKey, bind: impl FnOnce(&ConfigKey) -> Var<T>) -> Var<T> {
406 match self.values.entry(key) {
407 hash_map::Entry::Occupied(mut e) => {
408 if e.get().can_upgrade() {
409 if let Some(x) = e.get().as_any().downcast_ref::<ConfigVar<T>>() {
410 if let Some(var) = x.var.upgrade() {
411 return var;
412 }
413 } else {
414 tracing::error!(
415 "cannot get key `{}` as `{}` because it is already requested with a different type",
416 e.key(),
417 std::any::type_name::<T>()
418 );
419 return bind(e.key());
420 }
421 }
422 let cfg = bind(e.key());
424
425 let res = var(cfg.get());
426 let binding = res.bind_map_bidi(
427 &cfg,
428 clmv!(cfg, |v| {
429 let _strong_ref = &cfg;
430 v.clone()
431 }),
432 Clone::clone,
433 );
434
435 e.insert(ConfigVar::new_any(res.downgrade(), binding));
436 res
437 }
438 hash_map::Entry::Vacant(e) => {
439 let cfg = bind(e.key());
440 let res = var(cfg.get());
441 let binding = res.bind_map_bidi(
442 &cfg,
443 clmv!(cfg, |v| {
444 let _strong_ref = &cfg;
445 v.clone()
446 }),
447 Clone::clone,
448 );
449
450 e.insert(ConfigVar::new_any(res.downgrade(), binding));
451 res
452 }
453 }
454 }
455
456 pub fn get_or_bind_contains(&mut self, key: ConfigKey, bind: impl FnOnce(&ConfigKey) -> Var<bool>) -> Var<bool> {
458 match self.contains.entry(key) {
459 hash_map::Entry::Occupied(mut e) => {
460 if let Some(res) = e.get().var.upgrade() {
461 return res;
462 }
463
464 let cfg = bind(e.key());
465 let res = var(cfg.get());
466
467 let binding = VarHandles::from([
468 cfg.bind(&res),
469 res.hook(move |_| {
470 let _strong_ref = &cfg;
471 true
472 }),
473 ]);
474
475 e.insert(ConfigContainsVar {
476 var: res.downgrade(),
477 binding,
478 });
479
480 res
481 }
482 hash_map::Entry::Vacant(e) => {
483 let cfg = bind(e.key());
484 let res = var(cfg.get());
485
486 let binding = VarHandles::from([
487 cfg.bind(&res),
488 res.hook(move |_| {
489 let _strong_ref = &cfg;
490 true
491 }),
492 ]);
493
494 e.insert(ConfigContainsVar {
495 var: res.downgrade(),
496 binding,
497 });
498
499 res
500 }
501 }
502 }
503
504 pub fn rebind(&mut self, source: &mut dyn AnyConfig) {
509 self.values.retain(|key, wk_var| wk_var.rebind(key, source));
510 self.contains.retain(|key, wk_var| wk_var.rebind(key, source));
511 }
512
513 pub fn low_memory(&mut self) {
515 self.values.retain(|_, v| v.can_upgrade());
516 self.contains.retain(|_, v| v.var.strong_count() > 0)
517 }
518}
519trait AnyConfigVar: Any + Send + Sync {
520 fn as_any(&self) -> &dyn Any;
521 fn can_upgrade(&self) -> bool;
522 fn rebind(&mut self, key: &ConfigKey, source: &mut dyn AnyConfig) -> bool;
523}
524impl<T: ConfigValue> AnyConfigVar for ConfigVar<T> {
525 fn as_any(&self) -> &dyn Any {
526 self
527 }
528
529 fn can_upgrade(&self) -> bool {
530 self.var.strong_count() > 0
531 }
532
533 fn rebind(&mut self, key: &ConfigKey, source: &mut dyn AnyConfig) -> bool {
534 let var = if let Some(var) = self.var.upgrade() {
535 var
536 } else {
537 return false;
539 };
540
541 let source_var = source.get_raw(key.clone(), RawConfigValue::serialize(var.get()).unwrap(), false);
543
544 var.modify(clmv!(source_var, key, |vm| {
546 match RawConfigValue::deserialize::<T>(source_var.get()) {
547 Ok(value) => {
548 vm.set(value);
549 }
550 Err(e) => {
551 tracing::error!("rebind config get({key:?}) error, {e:?}");
553
554 source_var.set(RawConfigValue::serialize(vm.value()).unwrap());
556 }
557 }
558 }));
559
560 let mut first = true;
561 self.binding = source_var.bind_filter_map_bidi(
562 &var,
563 clmv!(key, |raw| {
565 match RawConfigValue::deserialize(raw.clone()) {
566 Ok(value) => Some(value),
567 Err(e) => {
568 tracing::error!("rebind config get({key:?}) error, {e:?}");
569 None
570 }
571 }
572 }),
573 clmv!(key, source_var, |value| {
575 if std::mem::take(&mut first) {
576 return None; }
578
579 let _strong_ref = &source_var;
580 match RawConfigValue::serialize(value) {
581 Ok(raw) => Some(raw),
582 Err(e) => {
583 tracing::error!("rebind config set({key:?}) error, {e:?}");
584 None
585 }
586 }
587 }),
588 );
589
590 true
591 }
592}
593impl ConfigContainsVar {
594 fn rebind(&mut self, key: &ConfigKey, source: &mut dyn AnyConfig) -> bool {
595 if let Some(res) = self.var.upgrade() {
596 let cfg = source.contains_key(key.clone());
597 res.set_from(&cfg);
598
599 self.binding = VarHandles::from([
600 cfg.bind(&res),
601 res.hook(move |_| {
602 let _strong_ref = &cfg;
603 true
604 }),
605 ]);
606
607 true
608 } else {
609 false
610 }
611 }
612}
613
614#[derive(Debug, Clone)]
616pub enum ConfigStatus {
617 Loaded,
619 Loading,
621 Saving,
623 LoadErrors(ConfigStatusError),
625 SaveErrors(ConfigStatusError),
627}
628impl ConfigStatus {
629 pub fn is_idle(&self) -> bool {
631 !matches!(self, Self::Loading | Self::Saving)
632 }
633
634 pub fn is_err(&self) -> bool {
636 matches!(self, ConfigStatus::LoadErrors(_) | ConfigStatus::SaveErrors(_))
637 }
638
639 pub fn errors(&self) -> &[Arc<dyn std::error::Error + Send + Sync>] {
645 match self {
646 ConfigStatus::LoadErrors(e) => e,
647 ConfigStatus::SaveErrors(e) => e,
648 _ => &[],
649 }
650 }
651
652 pub fn merge_status(status: impl Iterator<Item = ConfigStatus>) -> ConfigStatus {
654 let mut load_errors = vec![];
655 let mut save_errors = vec![];
656 let mut loading = false;
657 let mut saving = false;
658 for s in status {
659 match s {
660 ConfigStatus::Loaded => {}
661 ConfigStatus::Loading => loading = true,
662 ConfigStatus::Saving => saving = true,
663 ConfigStatus::LoadErrors(e) => {
664 if load_errors.is_empty() {
665 load_errors = e;
666 } else {
667 load_errors.extend(e);
668 }
669 }
670 ConfigStatus::SaveErrors(e) => {
671 if save_errors.is_empty() {
672 save_errors = e;
673 } else {
674 save_errors.extend(e);
675 }
676 }
677 }
678 }
679
680 if loading {
681 ConfigStatus::Loading
682 } else if saving {
683 ConfigStatus::Saving
684 } else if !load_errors.is_empty() {
685 ConfigStatus::LoadErrors(load_errors)
686 } else if !save_errors.is_empty() {
687 ConfigStatus::SaveErrors(save_errors)
688 } else {
689 ConfigStatus::Loaded
690 }
691 }
692}
693impl fmt::Display for ConfigStatus {
694 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
695 match self {
696 Self::Loaded => Ok(()),
697 Self::Loading => write!(f, "loading…"),
698 Self::Saving => write!(f, "saving…"),
699 Self::LoadErrors(e) => {
700 writeln!(f, "read errors:")?;
701 for e in e {
702 writeln!(f, " {e}")?;
703 }
704 Ok(())
705 }
706 Self::SaveErrors(e) => {
707 writeln!(f, "write errors:")?;
708 for e in e {
709 writeln!(f, " {e}")?;
710 }
711 Ok(())
712 }
713 }
714 }
715}
716impl PartialEq for ConfigStatus {
717 fn eq(&self, other: &Self) -> bool {
718 match (self, other) {
719 (Self::LoadErrors(a), Self::LoadErrors(b)) => a.is_empty() && b.is_empty(),
720 (Self::SaveErrors(a), Self::SaveErrors(b)) => a.is_empty() && b.is_empty(),
721 _ => core::mem::discriminant(self) == core::mem::discriminant(other),
722 }
723 }
724}
725impl Eq for ConfigStatus {}
726impl WatcherSyncStatus<ConfigStatusError, ConfigStatusError> for ConfigStatus {
727 fn writing() -> Self {
728 ConfigStatus::Saving
729 }
730
731 fn write_error(e: ConfigStatusError) -> Self {
732 ConfigStatus::SaveErrors(e)
733 }
734}
735impl WatcherReadStatus<ConfigStatusError> for ConfigStatus {
736 fn idle() -> Self {
737 ConfigStatus::Loaded
738 }
739
740 fn reading() -> Self {
741 ConfigStatus::Loading
742 }
743
744 fn read_error(e: ConfigStatusError) -> Self {
745 ConfigStatus::LoadErrors(e)
746 }
747}
748type ConfigStatusError = Vec<Arc<dyn std::error::Error + Send + Sync>>;