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 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::{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::{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)]
73#[non_exhaustive]
74pub struct ConfigManager {}
75
76impl AppExtension for ConfigManager {
77 fn event_preview(&mut self, update: &mut EventUpdate) {
78 if LOW_MEMORY_EVENT.on(update).is_some() {
79 CONFIG_SV.write().low_memory();
80 }
81 }
82}
83
84pub struct CONFIG;
91impl CONFIG {
92 pub fn load(&self, source: impl AnyConfig) {
97 CONFIG_SV.write().load(source)
98 }
99
100 pub fn status(&self) -> Var<ConfigStatus> {
102 CONFIG_SV.read().status()
103 }
104
105 pub async fn wait_idle(&self) {
109 task::yield_now().await; self.status().wait_match(|s| s.is_idle()).await;
111 }
112
113 pub fn get<T: ConfigValue>(&self, key: impl Into<ConfigKey>, default: T) -> Var<T> {
121 CONFIG_SV.write().get(key.into(), default, false)
122 }
123
124 pub fn insert<T: ConfigValue>(&self, key: impl Into<ConfigKey>, value: T) -> Var<T> {
127 CONFIG_SV.write().get(key.into(), value, true)
128 }
129}
130impl AnyConfig for CONFIG {
131 fn get_raw(&mut self, key: ConfigKey, default: RawConfigValue, insert: bool) -> Var<RawConfigValue> {
132 CONFIG_SV.write().get_raw(key, default, insert)
133 }
134
135 fn contains_key(&mut self, key: ConfigKey) -> Var<bool> {
136 CONFIG_SV.write().contains_key(key)
137 }
138
139 fn status(&self) -> Var<ConfigStatus> {
140 CONFIG.status()
141 }
142
143 fn remove(&mut self, key: &ConfigKey) -> bool {
144 CONFIG_SV.write().remove(key)
145 }
146
147 fn low_memory(&mut self) {
148 CONFIG_SV.write().low_memory()
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#[repr(transparent)]
168#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
169#[serde(transparent)]
170pub struct RawConfigValue(pub serde_value::Value);
171impl RawConfigValue {
172 pub fn serialize<T: serde::Serialize>(value: T) -> Result<Self, serde_value::SerializerError> {
174 serde_value::to_value(value).map(Self)
175 }
176
177 pub fn deserialize<T: serde::de::DeserializeOwned>(self) -> Result<T, serde_value::DeserializerError> {
179 T::deserialize(self.0)
180 }
181}
182
183pub trait AnyConfig: Send + Any {
187 fn status(&self) -> Var<ConfigStatus>;
189
190 fn get_raw(&mut self, key: ConfigKey, default: RawConfigValue, insert: bool) -> Var<RawConfigValue>;
198
199 fn contains_key(&mut self, key: ConfigKey) -> Var<bool>;
201
202 fn remove(&mut self, key: &ConfigKey) -> bool;
210
211 fn low_memory(&mut self);
213}
214
215pub trait Config: AnyConfig {
219 fn get<T: ConfigValue>(&mut self, key: impl Into<ConfigKey>, default: T, insert: bool) -> Var<T>;
227}
228impl<C: AnyConfig> Config for C {
229 fn get<T: ConfigValue>(&mut self, key: impl Into<ConfigKey>, default: T, insert: bool) -> Var<T> {
230 get_impl(self, insert, key.into(), default)
231 }
232}
233fn get_impl<T: ConfigValue, C: AnyConfig>(source: &mut C, insert: bool, key: ConfigKey, default: T) -> Var<T> {
234 source
235 .get_raw(key, RawConfigValue::serialize(&default).unwrap(), insert)
236 .filter_map_bidi(
237 move |raw| match raw.clone().deserialize() {
238 Ok(v) => Some(v),
239 Err(e) => {
240 #[cfg(debug_assertions)]
241 tracing::error!(
242 "failed to get config as `{}`, raw value was {:?}, {e}",
243 std::any::type_name::<T>(),
244 raw
245 );
246 #[cfg(not(debug_assertions))]
247 tracing::error!("failed to get config, {e}");
248 None
249 }
250 },
251 |v| match RawConfigValue::serialize(v) {
252 Ok(v) => Some(v),
253 Err(e) => {
254 tracing::error!("failed to set config, {e}");
255 None
256 }
257 },
258 move || default.clone(),
259 )
260}
261
262pub struct ReadOnlyConfig<C: Config> {
264 cfg: C,
265}
266impl<C: Config> ReadOnlyConfig<C> {
267 pub fn new(cfg: C) -> Self {
269 Self { cfg }
270 }
271}
272impl<C: Config> AnyConfig for ReadOnlyConfig<C> {
273 fn get_raw(&mut self, key: ConfigKey, default: RawConfigValue, _: bool) -> Var<RawConfigValue> {
274 self.cfg.get_raw(key, default, false).read_only()
275 }
276
277 fn contains_key(&mut self, key: ConfigKey) -> Var<bool> {
278 self.cfg.contains_key(key)
279 }
280
281 fn status(&self) -> Var<ConfigStatus> {
282 self.cfg.status()
283 }
284
285 fn remove(&mut self, _key: &ConfigKey) -> bool {
286 false
287 }
288
289 fn low_memory(&mut self) {
290 self.cfg.low_memory()
291 }
292}
293
294#[derive(Default)]
298pub struct MemoryConfig {
299 values: HashMap<ConfigKey, Var<RawConfigValue>>,
300 contains: HashMap<ConfigKey, WeakVar<bool>>,
301}
302
303impl AnyConfig for MemoryConfig {
304 fn status(&self) -> Var<ConfigStatus> {
305 const_var(ConfigStatus::Loaded)
306 }
307
308 fn get_raw(&mut self, key: ConfigKey, default: RawConfigValue, _insert: bool) -> Var<RawConfigValue> {
309 match self.values.entry(key) {
310 hash_map::Entry::Occupied(e) => e.get().clone(),
311 hash_map::Entry::Vacant(e) => {
312 let r = var(default);
313
314 if let Some(v) = self.contains.get(e.key())
315 && let Some(v) = v.upgrade()
316 {
317 v.set(true);
318 }
319
320 e.insert(r).clone()
321 }
322 }
323 }
324
325 fn contains_key(&mut self, key: ConfigKey) -> Var<bool> {
326 match self.contains.entry(key) {
327 hash_map::Entry::Occupied(mut e) => {
328 if let Some(r) = e.get().upgrade() {
329 r
330 } else {
331 let r = var(self.values.contains_key(e.key()));
332 e.insert(r.downgrade());
333 r
334 }
335 }
336 hash_map::Entry::Vacant(e) => {
337 let r = var(self.values.contains_key(e.key()));
338 e.insert(r.downgrade());
339 r
340 }
341 }
342 }
343
344 fn remove(&mut self, key: &ConfigKey) -> bool {
345 if self.values.remove(key).is_some() {
346 self.contains.retain(|_, v| v.strong_count() > 0);
347
348 if let Some(v) = self.contains.get(key)
349 && let Some(v) = v.upgrade()
350 {
351 v.set(false);
352 }
353 true
354 } else {
355 false
356 }
357 }
358
359 fn low_memory(&mut self) {
360 self.contains.retain(|_, v| v.strong_count() > 0);
361 }
362}
363
364struct ConfigVar<T: ConfigValue> {
365 var: WeakVar<T>,
366 binding: VarHandles,
367}
368impl<T: ConfigValue> ConfigVar<T> {
369 fn new_any(var: WeakVar<T>, binding: VarHandles) -> Box<dyn AnyConfigVar> {
370 Box::new(Self { var, binding })
371 }
372}
373struct ConfigContainsVar {
374 var: WeakVar<bool>,
375 binding: VarHandles,
376}
377
378#[derive(Default)]
382pub struct ConfigVars {
383 values: HashMap<ConfigKey, Box<dyn AnyConfigVar>>,
384 contains: HashMap<ConfigKey, ConfigContainsVar>,
385}
386impl ConfigVars {
387 pub fn get_or_bind<T: ConfigValue>(&mut self, key: ConfigKey, bind: impl FnOnce(&ConfigKey) -> Var<T>) -> Var<T> {
389 match self.values.entry(key) {
390 hash_map::Entry::Occupied(mut e) => {
391 if e.get().can_upgrade() {
392 if let Some(x) = e.get().as_any().downcast_ref::<ConfigVar<T>>() {
393 if let Some(var) = x.var.upgrade() {
394 return var;
395 }
396 } else {
397 tracing::error!(
398 "cannot get key `{}` as `{}` because it is already requested with a different type",
399 e.key(),
400 std::any::type_name::<T>()
401 );
402 return bind(e.key());
403 }
404 }
405 let cfg = bind(e.key());
407
408 let res = var(cfg.get());
409 let binding = res.bind_map_bidi(
410 &cfg,
411 clmv!(cfg, |v| {
412 let _strong_ref = &cfg;
413 v.clone()
414 }),
415 Clone::clone,
416 );
417
418 e.insert(ConfigVar::new_any(res.downgrade(), binding));
419 res
420 }
421 hash_map::Entry::Vacant(e) => {
422 let cfg = bind(e.key());
423 let res = var(cfg.get());
424 let binding = res.bind_map_bidi(
425 &cfg,
426 clmv!(cfg, |v| {
427 let _strong_ref = &cfg;
428 v.clone()
429 }),
430 Clone::clone,
431 );
432
433 e.insert(ConfigVar::new_any(res.downgrade(), binding));
434 res
435 }
436 }
437 }
438
439 pub fn get_or_bind_contains(&mut self, key: ConfigKey, bind: impl FnOnce(&ConfigKey) -> Var<bool>) -> Var<bool> {
441 match self.contains.entry(key) {
442 hash_map::Entry::Occupied(mut e) => {
443 if let Some(res) = e.get().var.upgrade() {
444 return res;
445 }
446
447 let cfg = bind(e.key());
448 let res = var(cfg.get());
449
450 let binding = VarHandles::from([
451 cfg.bind(&res),
452 res.hook(move |_| {
453 let _strong_ref = &cfg;
454 true
455 }),
456 ]);
457
458 e.insert(ConfigContainsVar {
459 var: res.downgrade(),
460 binding,
461 });
462
463 res
464 }
465 hash_map::Entry::Vacant(e) => {
466 let cfg = bind(e.key());
467 let res = var(cfg.get());
468
469 let binding = VarHandles::from([
470 cfg.bind(&res),
471 res.hook(move |_| {
472 let _strong_ref = &cfg;
473 true
474 }),
475 ]);
476
477 e.insert(ConfigContainsVar {
478 var: res.downgrade(),
479 binding,
480 });
481
482 res
483 }
484 }
485 }
486
487 pub fn rebind(&mut self, source: &mut dyn AnyConfig) {
492 self.values.retain(|key, wk_var| wk_var.rebind(key, source));
493 self.contains.retain(|key, wk_var| wk_var.rebind(key, source));
494 }
495
496 pub fn low_memory(&mut self) {
498 self.values.retain(|_, v| v.can_upgrade());
499 self.contains.retain(|_, v| v.var.strong_count() > 0)
500 }
501}
502trait AnyConfigVar: Any + Send + Sync {
503 fn as_any(&self) -> &dyn Any;
504 fn can_upgrade(&self) -> bool;
505 fn rebind(&mut self, key: &ConfigKey, source: &mut dyn AnyConfig) -> bool;
506}
507impl<T: ConfigValue> AnyConfigVar for ConfigVar<T> {
508 fn as_any(&self) -> &dyn Any {
509 self
510 }
511
512 fn can_upgrade(&self) -> bool {
513 self.var.strong_count() > 0
514 }
515
516 fn rebind(&mut self, key: &ConfigKey, source: &mut dyn AnyConfig) -> bool {
517 let var = if let Some(var) = self.var.upgrade() {
518 var
519 } else {
520 return false;
522 };
523
524 let source_var = source.get_raw(key.clone(), RawConfigValue::serialize(var.get()).unwrap(), false);
526
527 var.modify(clmv!(source_var, key, |vm| {
529 match RawConfigValue::deserialize::<T>(source_var.get()) {
530 Ok(value) => {
531 vm.set(value);
532 }
533 Err(e) => {
534 tracing::error!("rebind config get({key:?}) error, {e:?}");
536
537 source_var.set(RawConfigValue::serialize(vm.value()).unwrap());
539 }
540 }
541 }));
542
543 let mut first = true;
544 self.binding = source_var.bind_filter_map_bidi(
545 &var,
546 clmv!(key, |raw| {
548 match RawConfigValue::deserialize(raw.clone()) {
549 Ok(value) => Some(value),
550 Err(e) => {
551 tracing::error!("rebind config get({key:?}) error, {e:?}");
552 None
553 }
554 }
555 }),
556 clmv!(key, source_var, |value| {
558 if std::mem::take(&mut first) {
559 return None; }
561
562 let _strong_ref = &source_var;
563 match RawConfigValue::serialize(value) {
564 Ok(raw) => Some(raw),
565 Err(e) => {
566 tracing::error!("rebind config set({key:?}) error, {e:?}");
567 None
568 }
569 }
570 }),
571 );
572
573 true
574 }
575}
576impl ConfigContainsVar {
577 fn rebind(&mut self, key: &ConfigKey, source: &mut dyn AnyConfig) -> bool {
578 if let Some(res) = self.var.upgrade() {
579 let cfg = source.contains_key(key.clone());
580 res.set_from(&cfg);
581
582 self.binding = VarHandles::from([
583 cfg.bind(&res),
584 res.hook(move |_| {
585 let _strong_ref = &cfg;
586 true
587 }),
588 ]);
589
590 true
591 } else {
592 false
593 }
594 }
595}
596
597#[derive(Debug, Clone)]
599pub enum ConfigStatus {
600 Loaded,
602 Loading,
604 Saving,
606 LoadErrors(ConfigStatusError),
608 SaveErrors(ConfigStatusError),
610}
611impl ConfigStatus {
612 pub fn is_idle(&self) -> bool {
614 !matches!(self, Self::Loading | Self::Saving)
615 }
616
617 pub fn is_err(&self) -> bool {
619 matches!(self, ConfigStatus::LoadErrors(_) | ConfigStatus::SaveErrors(_))
620 }
621
622 pub fn errors(&self) -> &[Arc<dyn std::error::Error + Send + Sync>] {
628 match self {
629 ConfigStatus::LoadErrors(e) => e,
630 ConfigStatus::SaveErrors(e) => e,
631 _ => &[],
632 }
633 }
634
635 pub fn merge_status(status: impl Iterator<Item = ConfigStatus>) -> ConfigStatus {
637 let mut load_errors = vec![];
638 let mut save_errors = vec![];
639 let mut loading = false;
640 let mut saving = false;
641 for s in status {
642 match s {
643 ConfigStatus::Loaded => {}
644 ConfigStatus::Loading => loading = true,
645 ConfigStatus::Saving => saving = true,
646 ConfigStatus::LoadErrors(e) => {
647 if load_errors.is_empty() {
648 load_errors = e;
649 } else {
650 load_errors.extend(e);
651 }
652 }
653 ConfigStatus::SaveErrors(e) => {
654 if save_errors.is_empty() {
655 save_errors = e;
656 } else {
657 save_errors.extend(e);
658 }
659 }
660 }
661 }
662
663 if loading {
664 ConfigStatus::Loading
665 } else if saving {
666 ConfigStatus::Saving
667 } else if !load_errors.is_empty() {
668 ConfigStatus::LoadErrors(load_errors)
669 } else if !save_errors.is_empty() {
670 ConfigStatus::SaveErrors(save_errors)
671 } else {
672 ConfigStatus::Loaded
673 }
674 }
675}
676impl fmt::Display for ConfigStatus {
677 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
678 match self {
679 Self::Loaded => Ok(()),
680 Self::Loading => write!(f, "loading…"),
681 Self::Saving => write!(f, "saving…"),
682 Self::LoadErrors(e) => {
683 writeln!(f, "read errors:")?;
684 for e in e {
685 writeln!(f, " {e}")?;
686 }
687 Ok(())
688 }
689 Self::SaveErrors(e) => {
690 writeln!(f, "write errors:")?;
691 for e in e {
692 writeln!(f, " {e}")?;
693 }
694 Ok(())
695 }
696 }
697 }
698}
699impl PartialEq for ConfigStatus {
700 fn eq(&self, other: &Self) -> bool {
701 match (self, other) {
702 (Self::LoadErrors(a), Self::LoadErrors(b)) => a.is_empty() && b.is_empty(),
703 (Self::SaveErrors(a), Self::SaveErrors(b)) => a.is_empty() && b.is_empty(),
704 _ => core::mem::discriminant(self) == core::mem::discriminant(other),
705 }
706 }
707}
708impl Eq for ConfigStatus {}
709impl WatcherSyncStatus<ConfigStatusError, ConfigStatusError> for ConfigStatus {
710 fn writing() -> Self {
711 ConfigStatus::Saving
712 }
713
714 fn write_error(e: ConfigStatusError) -> Self {
715 ConfigStatus::SaveErrors(e)
716 }
717}
718impl WatcherReadStatus<ConfigStatusError> for ConfigStatus {
719 fn idle() -> Self {
720 ConfigStatus::Loaded
721 }
722
723 fn reading() -> Self {
724 ConfigStatus::Loading
725 }
726
727 fn read_error(e: ConfigStatusError) -> Self {
728 ConfigStatus::LoadErrors(e)
729 }
730}
731type ConfigStatusError = Vec<Arc<dyn std::error::Error + Send + Sync>>;