1use serde_json::{Map, Value as Json};
4
5use super::node::ModelNode;
6use crate::ModelError;
7
8const NEW_NODE: &str = "$new";
11
12#[derive(Clone, Debug, PartialEq, Eq)]
14#[cfg_attr(feature = "live", ravenrs_live::expose(part = "variants"))]
15pub enum ModelLayer {
16 Module(String),
18 Administrator,
20 User,
22}
23
24#[cfg_attr(feature = "live", ravenrs_live::expose)]
25impl ModelLayer {
26 fn rank(&self) -> u8 {
28 match self {
29 Self::Module(_) => 0,
30 Self::Administrator => 1,
31 Self::User => 2,
32 }
33 }
34}
35
36#[derive(Clone, Debug, Default, PartialEq)]
45#[cfg_attr(feature = "live", ravenrs_live::expose(part = "fields"))]
46pub struct Diff(Map<String, Json>);
47
48#[cfg_attr(feature = "live", ravenrs_live::expose)]
49impl Diff {
50 pub fn new() -> Self {
52 Self::default()
53 }
54
55 pub fn parse(text: &str) -> Result<Self, ModelError> {
57 match serde_json::from_str(text) {
58 Ok(Json::Object(map)) => Ok(Self(map)),
59 Ok(_) => Err(ModelError("a model difference has to be a JSON object".into())),
60 Err(e) => Err(ModelError(format!("a model difference is not valid JSON: {e}"))),
61 }
62 }
63
64 pub fn to_json(&self) -> String {
66 serde_json::to_string_pretty(&self.0).unwrap_or_else(|_| "{}".to_owned())
67 }
68
69 pub fn merge(&mut self, over: &Diff) {
71 fn merged(into: &mut Map<String, Json>, over: &Map<String, Json>) {
72 for (name, value) in over {
73 match (into.get_mut(name), value) {
74 (Some(Json::Object(mine)), Json::Object(theirs)) => merged(mine, theirs),
75 _ => {
76 into.insert(name.clone(), value.clone());
77 }
78 }
79 }
80 }
81 merged(&mut self.0, &over.0);
82 }
83
84 pub fn is_empty(&self) -> bool {
86 self.0.is_empty()
87 }
88
89 fn set(&mut self, path: &[&str], name: &str, value: Json) {
90 let mut map = &mut self.0;
91 for id in path {
92 let entry = map.entry((*id).to_owned()).or_insert_with(|| Json::Object(Map::new()));
93 if !entry.is_object() {
94 *entry = Json::Object(Map::new());
95 }
96 let Json::Object(next) = entry else { return };
97 map = next;
98 }
99 map.insert(name.to_owned(), value);
100 }
101
102 fn clear(&mut self, path: &[&str], name: &str) {
104 fn walk(map: &mut Map<String, Json>, path: &[&str], name: &str) {
105 match path.split_first() {
106 None => {
107 map.remove(name);
108 }
109 Some((id, rest)) => {
110 if let Some(Json::Object(next)) = map.get_mut(*id) {
111 walk(next, rest, name);
112 if next.is_empty() {
113 map.remove(*id);
114 }
115 }
116 }
117 }
118 }
119 walk(&mut self.0, path, name);
120 }
121}
122
123#[derive(Clone, Debug, PartialEq)]
126#[cfg_attr(feature = "live", ravenrs_live::expose(part = "fields"))]
127pub struct UnusableNode {
128 pub layer: ModelLayer,
130 pub path: Vec<String>,
132 pub difference: Json,
134}
135
136#[derive(Clone, Debug, PartialEq)]
138#[cfg_attr(feature = "live", ravenrs_live::expose(part = "fields"))]
139pub struct MergedModel {
140 pub root: ModelNode,
142 pub unusable: Vec<UnusableNode>,
144}
145
146fn apply(
147 node: &mut ModelNode,
148 difference: &Map<String, Json>,
149 layer: &ModelLayer,
150 path: &mut Vec<String>,
151 unusable: &mut Vec<UnusableNode>,
152) {
153 for (key, value) in difference {
154 match value {
155 Json::Object(inner) => {
156 let creates = inner.get(NEW_NODE).and_then(Json::as_bool).unwrap_or(false);
157 if creates && node.get(key).is_none() {
158 node.children.push(ModelNode::new(key));
159 }
160 path.push(key.clone());
161 match node.get_mut(key) {
162 Some(child) => apply(child, inner, layer, path, unusable),
163 None => unusable.push(UnusableNode {
164 layer: layer.clone(),
165 path: path.clone(),
166 difference: value.clone(),
167 }),
168 }
169 path.pop();
170 }
171 _ if key == NEW_NODE => {}
172 _ => {
173 node.properties.insert(key.clone(), value.clone());
174 }
175 }
176 }
177}
178
179#[derive(Clone, Debug, PartialEq)]
182#[cfg_attr(feature = "live", ravenrs_live::expose(part = "fields"))]
183pub struct ModelLayers {
184 zero: ModelNode,
185 layers: Vec<(ModelLayer, Diff)>,
186}
187
188#[cfg_attr(feature = "live", ravenrs_live::expose)]
189impl ModelLayers {
190 pub fn new(zero: ModelNode) -> Self {
192 Self { zero, layers: Vec::new() }
193 }
194
195 fn layer_mut(&mut self, layer: &ModelLayer) -> &mut Diff {
196 let at = match self.layers.iter().position(|(l, _)| l == layer) {
197 Some(at) => at,
198 None => {
199 let at = self.layers.partition_point(|(l, _)| l.rank() <= layer.rank());
200 self.layers.insert(at, (layer.clone(), Diff::new()));
201 at
202 }
203 };
204 &mut self.layers[at].1
205 }
206
207 pub fn set(&mut self, layer: &ModelLayer, path: &[&str], name: &str, value: Json) {
209 self.layer_mut(layer).set(path, name, value);
210 }
211
212 pub fn clear(&mut self, layer: &ModelLayer, path: &[&str], name: &str) {
214 self.layer_mut(layer).clear(path, name);
215 }
216
217 pub fn put(&mut self, layer: &ModelLayer, difference: Diff) {
219 *self.layer_mut(layer) = difference;
220 }
221
222 pub fn difference(&self, layer: &ModelLayer) -> Option<&Diff> {
224 self.layers.iter().find(|(l, _)| l == layer).map(|(_, d)| d)
225 }
226
227 pub fn merged(&self) -> MergedModel {
230 let mut root = self.zero.clone();
231 let mut unusable = Vec::new();
232 for (layer, difference) in &self.layers {
233 apply(&mut root, &difference.0, layer, &mut Vec::new(), &mut unusable);
234 }
235 MergedModel { root, unusable }
236 }
237
238 pub fn load(&mut self, layer: &ModelLayer, store: &dyn super::ModelStore) -> Result<(), ModelError> {
240 if let Some(difference) = store.load()? {
241 self.put(layer, difference);
242 }
243 Ok(())
244 }
245
246 pub fn save(&self, layer: &ModelLayer, store: &dyn super::ModelStore) -> Result<(), ModelError> {
248 store.save(self.difference(layer).unwrap_or(&Diff::new()))
249 }
250}
251
252#[cfg(test)]
253mod tests {
254 use super::*;
255
256 fn zero() -> ModelNode {
257 ModelNode::new("Application")
258 .child(ModelNode::new("Views").child(ModelNode::new("A_ListView").with("Caption", "zero")))
259 }
260
261 const PATH: [&str; 2] = ["Views", "A_ListView"];
262
263 fn caption(layers: &ModelLayers) -> String {
264 let merged = layers.merged();
265 merged.root.at(&PATH).and_then(|n| n.text("Caption")).unwrap_or("").to_owned()
266 }
267
268 #[test]
269 fn the_highest_layer_wins_whatever_order_they_came_in() {
270 let mut layers = ModelLayers::new(zero());
271 layers.set(&ModelLayer::User, &PATH, "Caption", "user".into());
272 layers.set(&ModelLayer::Administrator, &PATH, "Caption", "admin".into());
273 layers.set(&ModelLayer::Module("m".into()), &PATH, "Caption", "module".into());
274 assert_eq!(caption(&layers), "user");
275 }
276
277 #[test]
278 fn clearing_brings_back_the_layer_below() {
279 let mut layers = ModelLayers::new(zero());
280 layers.set(&ModelLayer::Administrator, &PATH, "Caption", "admin".into());
281 layers.set(&ModelLayer::User, &PATH, "Caption", "user".into());
282 layers.clear(&ModelLayer::User, &PATH, "Caption");
283 assert_eq!(caption(&layers), "admin");
284 layers.clear(&ModelLayer::Administrator, &PATH, "Caption");
285 assert_eq!(caption(&layers), "zero");
286 assert_eq!(layers.difference(&ModelLayer::User), Some(&Diff::new()), "nothing is left behind");
287 }
288
289 #[test]
290 fn modules_stack_in_the_order_they_first_came() {
291 let mut layers = ModelLayers::new(zero());
292 layers.set(&ModelLayer::Module("z".into()), &PATH, "Caption", "z".into());
293 layers.set(&ModelLayer::Module("a".into()), &PATH, "Caption", "a".into());
294 assert_eq!(caption(&layers), "a");
295 }
296
297 #[test]
298 fn a_setting_with_no_node_is_kept_and_comes_back() {
299 let mut layers = ModelLayers::new(zero());
300 layers.set(&ModelLayer::User, &["Views", "Gone_ListView"], "Caption", "kept".into());
301 let merged = layers.merged();
302 assert_eq!(merged.unusable.len(), 1);
303 assert_eq!(merged.unusable[0].path, ["Views", "Gone_ListView"]);
304 assert_eq!(merged.unusable[0].layer, ModelLayer::User);
305 let saved = layers.difference(&ModelLayer::User).expect("a layer").to_json();
307 let mut later = ModelLayers::new(zero());
308 later.put(&ModelLayer::User, Diff::parse(&saved).expect("valid"));
309 assert_eq!(later.merged().unusable.len(), 1, "still nowhere to go");
310 let with_node = {
311 let mut root = zero();
312 root.get_mut("Views").expect("views").children.push(ModelNode::new("Gone_ListView"));
313 let mut l = ModelLayers::new(root);
314 l.put(&ModelLayer::User, Diff::parse(&saved).expect("valid"));
315 l.merged()
316 };
317 assert!(with_node.unusable.is_empty());
318 assert_eq!(
319 with_node.root.at(&["Views", "Gone_ListView"]).and_then(|n| n.text("Caption")),
320 Some("kept")
321 );
322 }
323
324 #[test]
325 fn a_difference_can_create_a_node() {
326 let difference = Diff::parse(r#"{ "Views": { "B_ListView": { "$new": true, "Caption": "b" } } }"#);
327 let mut layers = ModelLayers::new(zero());
328 layers.put(&ModelLayer::Administrator, difference.expect("valid"));
329 let merged = layers.merged();
330 assert!(merged.unusable.is_empty());
331 let node = merged.root.at(&["Views", "B_ListView"]).expect("the new node");
332 assert_eq!(node.text("Caption"), Some("b"));
333 assert!(!node.properties.contains_key(NEW_NODE), "the mark is not a setting");
334 }
335
336 #[test]
337 fn bad_json_says_so() {
338 assert!(Diff::parse("[1]").is_err_and(|e| e.0.contains("object")));
339 assert!(Diff::parse("{").is_err_and(|e| e.0.contains("not valid")));
340 }
341}