zng_ext_l10n/sources/
swap.rs
1use std::{collections::HashMap, path::PathBuf, sync::Arc};
2
3use zng_var::{ArcEq, ArcVar, BoxedVar, BoxedWeakVar, Var as _, VarHandle, WeakVar as _, types::WeakArcVar, var};
4
5use crate::{L10nSource, Lang, LangFilePath, LangMap, LangResourceStatus};
6
7use super::NilL10nSource;
8
9pub struct SwapL10nSource {
16 actual: Box<dyn L10nSource>,
17
18 available_langs: ArcVar<Arc<LangMap<HashMap<LangFilePath, PathBuf>>>>,
19 available_langs_status: ArcVar<LangResourceStatus>,
20
21 res: HashMap<(Lang, LangFilePath), SwapFile>,
22}
23impl SwapL10nSource {
24 pub fn new() -> Self {
26 Self {
27 actual: Box::new(NilL10nSource),
28 available_langs: var(Arc::default()),
29 available_langs_status: var(LangResourceStatus::NotAvailable),
30 res: HashMap::new(),
31 }
32 }
33
34 pub fn load(&mut self, source: impl L10nSource) {
36 self.swap_source(Box::new(source))
37 }
38 fn swap_source(&mut self, new: Box<dyn L10nSource>) {
39 self.actual = new;
40
41 let actual_langs = self.actual.available_langs();
42 self.available_langs.set_from(&actual_langs);
43 actual_langs.bind(&self.available_langs).perm();
44
45 let actual_status = self.actual.available_langs_status();
46 self.available_langs_status.set_from(&actual_status);
47 actual_status.bind(&self.available_langs_status).perm();
48
49 for ((lang, file), f) in &mut self.res {
50 if let Some(res) = f.res.upgrade() {
51 let actual_f = self.actual.lang_resource(lang.clone(), file.clone());
52 f.actual_weak_res = actual_f.bind(&res); f.res_strong_actual = res.hold(actual_f); let actual_s = self.actual.lang_resource_status(lang.clone(), file.clone());
56 f.status.set_from(&actual_s);
57 f.actual_weak_status = actual_s.bind(&f.status);
58 } else {
59 f.status.set(LangResourceStatus::NotAvailable);
60 }
61 }
62 }
63}
64impl Default for SwapL10nSource {
65 fn default() -> Self {
66 Self::new()
67 }
68}
69impl L10nSource for SwapL10nSource {
70 fn available_langs(&mut self) -> BoxedVar<Arc<LangMap<HashMap<LangFilePath, PathBuf>>>> {
71 self.available_langs.read_only().boxed()
72 }
73
74 fn available_langs_status(&mut self) -> BoxedVar<LangResourceStatus> {
75 self.available_langs_status.read_only().boxed()
76 }
77
78 fn lang_resource(&mut self, lang: Lang, file: LangFilePath) -> BoxedVar<Option<ArcEq<fluent::FluentResource>>> {
79 match self.res.entry((lang, file)) {
80 std::collections::hash_map::Entry::Occupied(mut e) => {
81 if let Some(res) = e.get().res.upgrade() {
82 res
83 } else {
84 let (lang, file) = e.key();
85 let actual_f = self.actual.lang_resource(lang.clone(), file.clone());
86 let actual_s = self.actual.lang_resource_status(lang.clone(), file.clone());
87
88 let f = e.get_mut();
89
90 let res = var(actual_f.get());
91 f.actual_weak_res = actual_f.bind(&res); f.res_strong_actual = res.hold(actual_f); let res = res.boxed();
94 f.res = res.downgrade();
95
96 f.status.set_from(&actual_s);
97 f.actual_weak_status = actual_s.bind(&f.status);
98
99 res
100 }
101 }
102 std::collections::hash_map::Entry::Vacant(e) => {
103 let mut f = SwapFile::new();
104 let (lang, file) = e.key();
105 let actual_f = self.actual.lang_resource(lang.clone(), file.clone());
106 let actual_s = self.actual.lang_resource_status(lang.clone(), file.clone());
107
108 let res = var(actual_f.get());
109 f.actual_weak_res = actual_f.bind(&res); f.res_strong_actual = res.hold(actual_f); let res = res.boxed();
112 f.res = res.downgrade();
113
114 f.status.set_from(&actual_s);
115 f.actual_weak_status = actual_s.bind(&f.status);
116
117 e.insert(f);
118
119 res
120 }
121 }
122 }
123
124 fn lang_resource_status(&mut self, lang: Lang, file: LangFilePath) -> BoxedVar<LangResourceStatus> {
125 self.res
126 .entry((lang, file))
127 .or_insert_with(SwapFile::new)
128 .status
129 .read_only()
130 .boxed()
131 }
132}
133struct SwapFile {
134 res: BoxedWeakVar<Option<ArcEq<fluent::FluentResource>>>,
135 status: ArcVar<LangResourceStatus>,
136 actual_weak_res: VarHandle,
137 res_strong_actual: VarHandle,
138 actual_weak_status: VarHandle,
139}
140impl SwapFile {
141 fn new() -> Self {
142 Self {
143 res: WeakArcVar::default().boxed(),
144 status: var(LangResourceStatus::Loading),
145 actual_weak_res: VarHandle::dummy(),
146 res_strong_actual: VarHandle::dummy(),
147 actual_weak_status: VarHandle::dummy(),
148 }
149 }
150}