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")))]
15#![expect(clippy::type_complexity)]
17#![warn(unused_extern_crates)]
18#![warn(missing_docs)]
19
20mod serde_value;
21
22mod fallback;
23pub use fallback::*;
24
25mod swap;
26pub use swap::*;
27
28mod switch;
29pub use switch::*;
30
31mod sync;
32pub use sync::*;
33
34#[cfg(feature = "json")]
35mod json;
36#[cfg(feature = "json")]
37pub use json::*;
38
39#[cfg(feature = "toml")]
40mod toml;
41#[cfg(feature = "toml")]
42pub use self::toml::*;
43
44#[cfg(feature = "ron")]
45mod ron;
46#[cfg(feature = "ron")]
47pub use self::ron::*;
48
49#[cfg(feature = "yaml")]
50mod yaml;
51#[cfg(feature = "yaml")]
52pub use self::yaml::*;
53
54pub mod settings;
55
56use std::{
57 any::Any,
58 collections::{HashMap, hash_map},
59 fmt, io,
60 sync::Arc,
61};
62
63use zng_app::view_process::raw_events::LOW_MEMORY_EVENT;
64use zng_app_context::app_local;
65use zng_clone_move::clmv;
66use zng_ext_fs_watcher::{WatchFile, WatcherReadStatus, WatcherSyncStatus, WriteFile};
67use zng_task as task;
68use zng_txt::Txt;
69use zng_var::{Var, VarHandles, VarValue, WeakVar, const_var, var};
70
71pub struct CONFIG;
78impl CONFIG {
79 pub fn load(&self, source: impl AnyConfig) {
84 CONFIG_SV.write().load(source)
85 }
86
87 pub fn status(&self) -> Var<ConfigStatus> {
89 CONFIG_SV.read().status()
90 }
91
92 pub async fn wait_idle(&self) {
96 task::yield_now().await; self.status().wait_match(|s| s.is_idle()).await;
98 }
99
100 pub fn get<T: ConfigValue>(&self, key: impl Into<ConfigKey>, default: T) -> Var<T> {
108 CONFIG_SV.write().get(key.into(), default, false)
109 }
110
111 pub fn insert<T: ConfigValue>(&self, key: impl Into<ConfigKey>, value: T) -> Var<T> {
114 CONFIG_SV.write().get(key.into(), value, true)
115 }
116}
117impl AnyConfig for CONFIG {
118 fn get_raw(&mut self, key: ConfigKey, default: RawConfigValue, insert: bool) -> Var<RawConfigValue> {
119 CONFIG_SV.write().get_raw(key, default, insert)
120 }
121
122 fn contains_key(&mut self, key: ConfigKey) -> Var<bool> {
123 CONFIG_SV.write().contains_key(key)
124 }
125
126 fn status(&self) -> Var<ConfigStatus> {
127 CONFIG.status()
128 }
129
130 fn remove(&mut self, key: &ConfigKey) -> bool {
131 CONFIG_SV.write().remove(key)
132 }
133
134 fn low_memory(&mut self) {
135 CONFIG_SV.write().low_memory()
136 }
137}
138
139app_local! {
140 static CONFIG_SV: SwapConfig = {
141 hooks();
142 SwapConfig::new()
143 };
144}
145fn hooks() {
146 LOW_MEMORY_EVENT
147 .hook(|_| {
148 CONFIG_SV.write().low_memory();
149 true
150 })
151 .perm();
152}
153
154pub type ConfigKey = Txt;
156
157#[diagnostic::on_unimplemented(note = "`ConfigValue` is implemented for all `T: VarValue + Serialize + DeserializeOwned`")]
161pub trait ConfigValue: VarValue + serde::Serialize + serde::de::DeserializeOwned {}
162impl<T: VarValue + serde::Serialize + serde::de::DeserializeOwned> ConfigValue for T {}
163
164#[repr(transparent)]
166#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
167#[serde(transparent)]
168pub struct RawConfigValue(pub serde_value::Value);
169impl RawConfigValue {
170 pub fn serialize<T: serde::Serialize>(value: T) -> Result<Self, serde_value::SerializerError> {
172 serde_value::to_value(value).map(Self)
173 }
174
175 pub fn deserialize<T: serde::de::DeserializeOwned>(self) -> Result<T, serde_value::DeserializerError> {
177 T::deserialize(self.0)
178 }
179}
180
181pub trait AnyConfig: Send + Any {
185 fn status(&self) -> Var<ConfigStatus>;
187
188 fn get_raw(&mut self, key: ConfigKey, default: RawConfigValue, insert: bool) -> Var<RawConfigValue>;
196
197 fn contains_key(&mut self, key: ConfigKey) -> Var<bool>;
199
200 fn remove(&mut self, key: &ConfigKey) -> bool;
208
209 fn low_memory(&mut self);
211}
212
213pub trait Config: AnyConfig {
217 fn get<T: ConfigValue>(&mut self, key: impl Into<ConfigKey>, default: T, insert: bool) -> Var<T>;
225}
226impl<C: AnyConfig> Config for C {
227 fn get<T: ConfigValue>(&mut self, key: impl Into<ConfigKey>, default: T, insert: bool) -> Var<T> {
228 get_impl(self, insert, key.into(), default)
229 }
230}
231fn get_impl<T: ConfigValue, C: AnyConfig>(source: &mut C, insert: bool, key: ConfigKey, default: T) -> Var<T> {
232 source
233 .get_raw(key, RawConfigValue::serialize(&default).unwrap(), insert)
234 .filter_map_bidi(
235 move |raw| match raw.clone().deserialize() {
236 Ok(v) => Some(v),
237 Err(e) => {
238 #[cfg(debug_assertions)]
239 tracing::error!(
240 "failed to get config as `{}`, raw value was {:?}, {e}",
241 std::any::type_name::<T>(),
242 raw
243 );
244 #[cfg(not(debug_assertions))]
245 tracing::error!("failed to get config, {e}");
246 None
247 }
248 },
249 |v| match RawConfigValue::serialize(v) {
250 Ok(v) => Some(v),
251 Err(e) => {
252 tracing::error!("failed to set config, {e}");
253 None
254 }
255 },
256 move || default.clone(),
257 )
258}
259
260pub struct ReadOnlyConfig<C: Config> {
265 cfg: C,
266}
267impl<C: Config> ReadOnlyConfig<C> {
268 pub fn new(cfg: C) -> Self {
270 Self { cfg }
271 }
272}
273impl<C: Config> AnyConfig for ReadOnlyConfig<C> {
274 fn get_raw(&mut self, key: ConfigKey, default: RawConfigValue, _: bool) -> Var<RawConfigValue> {
275 self.cfg.get_raw(key, default, false).read_only()
276 }
277
278 fn contains_key(&mut self, key: ConfigKey) -> Var<bool> {
279 self.cfg.contains_key(key)
280 }
281
282 fn status(&self) -> Var<ConfigStatus> {
283 self.cfg.status()
284 }
285
286 fn remove(&mut self, _key: &ConfigKey) -> bool {
287 false
288 }
289
290 fn low_memory(&mut self) {
291 self.cfg.low_memory()
292 }
293}
294
295#[derive(Default)]
299pub struct MemoryConfig {
300 values: HashMap<ConfigKey, Var<RawConfigValue>>,
301 contains: HashMap<ConfigKey, WeakVar<bool>>,
302}
303
304impl AnyConfig for MemoryConfig {
305 fn status(&self) -> Var<ConfigStatus> {
306 const_var(ConfigStatus::Loaded)
307 }
308
309 fn get_raw(&mut self, key: ConfigKey, default: RawConfigValue, _insert: bool) -> Var<RawConfigValue> {
310 match self.values.entry(key) {
311 hash_map::Entry::Occupied(e) => e.get().clone(),
312 hash_map::Entry::Vacant(e) => {
313 let r = var(default);
314
315 if let Some(v) = self.contains.get(e.key())
316 && let Some(v) = v.upgrade()
317 {
318 v.set(true);
319 }
320
321 e.insert(r).clone()
322 }
323 }
324 }
325
326 fn contains_key(&mut self, key: ConfigKey) -> Var<bool> {
327 match self.contains.entry(key) {
328 hash_map::Entry::Occupied(mut e) => {
329 if let Some(r) = e.get().upgrade() {
330 r
331 } else {
332 let r = var(self.values.contains_key(e.key()));
333 e.insert(r.downgrade());
334 r
335 }
336 }
337 hash_map::Entry::Vacant(e) => {
338 let r = var(self.values.contains_key(e.key()));
339 e.insert(r.downgrade());
340 r
341 }
342 }
343 }
344
345 fn remove(&mut self, key: &ConfigKey) -> bool {
346 if self.values.remove(key).is_some() {
347 self.contains.retain(|_, v| v.strong_count() > 0);
348
349 if let Some(v) = self.contains.get(key)
350 && let Some(v) = v.upgrade()
351 {
352 v.set(false);
353 }
354 true
355 } else {
356 false
357 }
358 }
359
360 fn low_memory(&mut self) {
361 self.contains.retain(|_, v| v.strong_count() > 0);
362 }
363}
364
365struct ConfigVar<T: ConfigValue> {
366 var: WeakVar<T>,
367 binding: VarHandles,
368}
369impl<T: ConfigValue> ConfigVar<T> {
370 fn new_any(var: WeakVar<T>, binding: VarHandles) -> Box<dyn AnyConfigVar> {
371 Box::new(Self { var, binding })
372 }
373}
374struct ConfigContainsVar {
375 var: WeakVar<bool>,
376 binding: VarHandles,
377}
378
379#[derive(Default)]
383pub struct ConfigVars {
384 values: HashMap<ConfigKey, Box<dyn AnyConfigVar>>,
385 contains: HashMap<ConfigKey, ConfigContainsVar>,
386}
387impl ConfigVars {
388 pub fn get_or_bind<T: ConfigValue>(&mut self, key: ConfigKey, bind: impl FnOnce(&ConfigKey) -> Var<T>) -> Var<T> {
390 match self.values.entry(key) {
391 hash_map::Entry::Occupied(mut e) => {
392 if e.get().can_upgrade() {
393 if let Some(x) = e.get().as_any().downcast_ref::<ConfigVar<T>>() {
394 if let Some(var) = x.var.upgrade() {
395 return var;
396 }
397 } else {
398 tracing::error!(
399 "cannot get key `{}` as `{}` because it is already requested with a different type",
400 e.key(),
401 std::any::type_name::<T>()
402 );
403 return bind(e.key());
404 }
405 }
406 let cfg = bind(e.key());
408
409 let res = var(cfg.get());
410 let binding = res.bind_map_bidi(
411 &cfg,
412 clmv!(cfg, |v| {
413 let _strong_ref = &cfg;
414 v.clone()
415 }),
416 Clone::clone,
417 );
418
419 e.insert(ConfigVar::new_any(res.downgrade(), binding));
420 res
421 }
422 hash_map::Entry::Vacant(e) => {
423 let cfg = bind(e.key());
424 let res = var(cfg.get());
425 let binding = res.bind_map_bidi(
426 &cfg,
427 clmv!(cfg, |v| {
428 let _strong_ref = &cfg;
429 v.clone()
430 }),
431 Clone::clone,
432 );
433
434 e.insert(ConfigVar::new_any(res.downgrade(), binding));
435 res
436 }
437 }
438 }
439
440 pub fn get_or_bind_contains(&mut self, key: ConfigKey, bind: impl FnOnce(&ConfigKey) -> Var<bool>) -> Var<bool> {
442 match self.contains.entry(key) {
443 hash_map::Entry::Occupied(mut e) => {
444 if let Some(res) = e.get().var.upgrade() {
445 return res;
446 }
447
448 let cfg = bind(e.key());
449 let res = var(cfg.get());
450
451 let binding = VarHandles::from([
452 cfg.bind(&res),
453 res.hook(move |_| {
454 let _strong_ref = &cfg;
455 true
456 }),
457 ]);
458
459 e.insert(ConfigContainsVar {
460 var: res.downgrade(),
461 binding,
462 });
463
464 res
465 }
466 hash_map::Entry::Vacant(e) => {
467 let cfg = bind(e.key());
468 let res = var(cfg.get());
469
470 let binding = VarHandles::from([
471 cfg.bind(&res),
472 res.hook(move |_| {
473 let _strong_ref = &cfg;
474 true
475 }),
476 ]);
477
478 e.insert(ConfigContainsVar {
479 var: res.downgrade(),
480 binding,
481 });
482
483 res
484 }
485 }
486 }
487
488 pub fn rebind(&mut self, source: &mut dyn AnyConfig) {
493 self.values.retain(|key, wk_var| wk_var.rebind(key, source));
494 self.contains.retain(|key, wk_var| wk_var.rebind(key, source));
495 }
496
497 pub fn low_memory(&mut self) {
499 self.values.retain(|_, v| v.can_upgrade());
500 self.contains.retain(|_, v| v.var.strong_count() > 0)
501 }
502}
503trait AnyConfigVar: Any + Send + Sync {
504 fn as_any(&self) -> &dyn Any;
505 fn can_upgrade(&self) -> bool;
506 fn rebind(&mut self, key: &ConfigKey, source: &mut dyn AnyConfig) -> bool;
507}
508impl<T: ConfigValue> AnyConfigVar for ConfigVar<T> {
509 fn as_any(&self) -> &dyn Any {
510 self
511 }
512
513 fn can_upgrade(&self) -> bool {
514 self.var.strong_count() > 0
515 }
516
517 fn rebind(&mut self, key: &ConfigKey, source: &mut dyn AnyConfig) -> bool {
518 let var = if let Some(var) = self.var.upgrade() {
519 var
520 } else {
521 return false;
523 };
524
525 let source_var = source.get_raw(key.clone(), RawConfigValue::serialize(var.get()).unwrap(), false);
527
528 var.modify(clmv!(source_var, key, |vm| {
530 match RawConfigValue::deserialize::<T>(source_var.get()) {
531 Ok(value) => {
532 vm.set(value);
533 }
534 Err(e) => {
535 tracing::error!("rebind config get({key:?}) error, {e:?}");
537
538 source_var.set(RawConfigValue::serialize(vm.value()).unwrap());
540 }
541 }
542 }));
543
544 let mut first = true;
545 self.binding = source_var.bind_filter_map_bidi(
546 &var,
547 clmv!(key, |raw| {
549 match RawConfigValue::deserialize(raw.clone()) {
550 Ok(value) => Some(value),
551 Err(e) => {
552 tracing::error!("rebind config get({key:?}) error, {e:?}");
553 None
554 }
555 }
556 }),
557 clmv!(key, source_var, |value| {
559 if std::mem::take(&mut first) {
560 return None; }
562
563 let _strong_ref = &source_var;
564 match RawConfigValue::serialize(value) {
565 Ok(raw) => Some(raw),
566 Err(e) => {
567 tracing::error!("rebind config set({key:?}) error, {e:?}");
568 None
569 }
570 }
571 }),
572 );
573
574 true
575 }
576}
577impl ConfigContainsVar {
578 fn rebind(&mut self, key: &ConfigKey, source: &mut dyn AnyConfig) -> bool {
579 if let Some(res) = self.var.upgrade() {
580 let cfg = source.contains_key(key.clone());
581 res.set_from(&cfg);
582
583 self.binding = VarHandles::from([
584 cfg.bind(&res),
585 res.hook(move |_| {
586 let _strong_ref = &cfg;
587 true
588 }),
589 ]);
590
591 true
592 } else {
593 false
594 }
595 }
596}
597
598#[derive(Debug, Clone)]
600pub enum ConfigStatus {
601 Loaded,
603 Loading,
605 Saving,
607 LoadErrors(ConfigStatusError),
609 SaveErrors(ConfigStatusError),
611}
612impl ConfigStatus {
613 pub fn is_idle(&self) -> bool {
615 !matches!(self, Self::Loading | Self::Saving)
616 }
617
618 pub fn is_err(&self) -> bool {
620 matches!(self, ConfigStatus::LoadErrors(_) | ConfigStatus::SaveErrors(_))
621 }
622
623 pub fn errors(&self) -> &[Arc<dyn std::error::Error + Send + Sync>] {
629 match self {
630 ConfigStatus::LoadErrors(e) => e,
631 ConfigStatus::SaveErrors(e) => e,
632 _ => &[],
633 }
634 }
635
636 pub fn merge_status(status: impl Iterator<Item = ConfigStatus>) -> ConfigStatus {
638 let mut load_errors = vec![];
639 let mut save_errors = vec![];
640 let mut loading = false;
641 let mut saving = false;
642 for s in status {
643 match s {
644 ConfigStatus::Loaded => {}
645 ConfigStatus::Loading => loading = true,
646 ConfigStatus::Saving => saving = true,
647 ConfigStatus::LoadErrors(e) => {
648 if load_errors.is_empty() {
649 load_errors = e;
650 } else {
651 load_errors.extend(e);
652 }
653 }
654 ConfigStatus::SaveErrors(e) => {
655 if save_errors.is_empty() {
656 save_errors = e;
657 } else {
658 save_errors.extend(e);
659 }
660 }
661 }
662 }
663
664 if loading {
665 ConfigStatus::Loading
666 } else if saving {
667 ConfigStatus::Saving
668 } else if !load_errors.is_empty() {
669 ConfigStatus::LoadErrors(load_errors)
670 } else if !save_errors.is_empty() {
671 ConfigStatus::SaveErrors(save_errors)
672 } else {
673 ConfigStatus::Loaded
674 }
675 }
676}
677impl fmt::Display for ConfigStatus {
678 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
679 match self {
680 Self::Loaded => Ok(()),
681 Self::Loading => write!(f, "loading…"),
682 Self::Saving => write!(f, "saving…"),
683 Self::LoadErrors(e) => {
684 writeln!(f, "read errors:")?;
685 for e in e {
686 writeln!(f, " {e}")?;
687 }
688 Ok(())
689 }
690 Self::SaveErrors(e) => {
691 writeln!(f, "write errors:")?;
692 for e in e {
693 writeln!(f, " {e}")?;
694 }
695 Ok(())
696 }
697 }
698 }
699}
700impl PartialEq for ConfigStatus {
701 fn eq(&self, other: &Self) -> bool {
702 match (self, other) {
703 (Self::LoadErrors(a), Self::LoadErrors(b)) => a.is_empty() && b.is_empty(),
704 (Self::SaveErrors(a), Self::SaveErrors(b)) => a.is_empty() && b.is_empty(),
705 _ => core::mem::discriminant(self) == core::mem::discriminant(other),
706 }
707 }
708}
709impl Eq for ConfigStatus {}
710impl WatcherSyncStatus<ConfigStatusError, ConfigStatusError> for ConfigStatus {
711 fn writing() -> Self {
712 ConfigStatus::Saving
713 }
714
715 fn write_error(e: ConfigStatusError) -> Self {
716 ConfigStatus::SaveErrors(e)
717 }
718}
719impl WatcherReadStatus<ConfigStatusError> for ConfigStatus {
720 fn idle() -> Self {
721 ConfigStatus::Loaded
722 }
723
724 fn reading() -> Self {
725 ConfigStatus::Loading
726 }
727
728 fn read_error(e: ConfigStatusError) -> Self {
729 ConfigStatus::LoadErrors(e)
730 }
731}
732type ConfigStatusError = Vec<Arc<dyn std::error::Error + Send + Sync>>;