ephemeris_documents/
merge.rs1use std::collections::HashSet;
4
5use crate::error::MergeError;
6use crate::ownership::{Claim, Ownership};
7use crate::path::KeyPath;
8
9pub(crate) trait Node: Clone {
11 fn adopt(&mut self, key: &str, source: &Self);
15 fn adopt_shell(&mut self, key: &str, source: &Self);
18 fn child(&self, key: &str) -> Option<&Self>;
19 fn child_mut(&mut self, key: &str) -> Option<&mut Self>;
20 fn is_container(&self) -> bool;
22 fn is_empty(&self) -> bool {
24 self.keys().next().is_none()
25 }
26 fn keys(&self) -> impl Iterator<Item = &str> + '_;
28 fn remove(&mut self, key: &str);
29}
30
31struct Merge<'a> {
32 ownership: &'a Ownership,
33}
34
35impl Merge<'_> {
36 fn container<N: Node>(
37 &self,
38 current: &mut N,
39 desired: Option<&N>,
40 path: &mut Vec<String>,
41 ) -> Result<(), MergeError> {
42 if !current.is_container() || desired.is_some_and(|node| !node.is_container()) {
43 return Err(MergeError::TypeConflict {
44 path: KeyPath::from(path.as_slice()),
45 });
46 }
47 let mut keys: Vec<String> = current.keys().map(str::to_owned).collect();
48 let added: Vec<String> = {
49 let known: HashSet<&str> = keys.iter().map(String::as_str).collect();
50 desired
51 .into_iter()
52 .flat_map(Node::keys)
53 .filter(|key| !known.contains(key))
54 .map(str::to_owned)
55 .collect()
56 };
57 keys.extend(added);
58 for key in &keys {
59 path.push(key.clone());
60 let result = self.entry(current, desired, key, path);
61 path.pop();
62 result?;
63 }
64 Ok(())
65 }
66
67 fn entry<N: Node>(
69 &self,
70 current: &mut N,
71 desired: Option<&N>,
72 key: &str,
73 path: &mut Vec<String>,
74 ) -> Result<(), MergeError> {
75 match self.ownership.claim(path) {
76 Claim::Owned => {
77 match desired.filter(|node| node.child(key).is_some()) {
78 Some(parent) => current.adopt(key, parent),
79 None => current.remove(key),
80 }
81 Ok(())
82 }
83 Claim::Unmanaged => Ok(()),
84 Claim::Mixed => self.mixed(current, desired, key, path),
85 }
86 }
87
88 fn mixed<N: Node>(
89 &self,
90 current: &mut N,
91 desired: Option<&N>,
92 key: &str,
93 path: &mut Vec<String>,
94 ) -> Result<(), MergeError> {
95 let current_child = current.child(key);
96 let current_is_container = current_child.map(Node::is_container);
97 match (
98 current_is_container,
99 desired.and_then(|node| node.child(key)),
100 ) {
101 (None, None) => Ok(()),
102 (None, Some(node)) => {
103 if !node.is_container() {
104 return Err(MergeError::TypeConflict {
105 path: KeyPath::from(path.as_slice()),
106 });
107 }
108 if let Some(parent) = desired {
109 current.adopt_shell(key, parent);
110 }
111 self.descend(current, Some(node), key, path)?;
112 let hollow = current.child(key).is_some_and(Node::is_empty);
113 if hollow && !node.is_empty() {
114 current.remove(key);
115 }
116 Ok(())
117 }
118 (Some(true), None) => self.descend(current, None, key, path),
119 (Some(false), None) => {
120 if self.ownership.default_claim().is_owned() {
121 current.remove(key);
122 }
123 Ok(())
124 }
125 (Some(_), Some(node)) => self.descend(current, Some(node), key, path),
126 }
127 }
128
129 fn descend<N: Node>(
130 &self,
131 current: &mut N,
132 desired: Option<&N>,
133 key: &str,
134 path: &mut Vec<String>,
135 ) -> Result<(), MergeError> {
136 current
137 .child_mut(key)
138 .map_or(Ok(()), |child| self.container(child, desired, path))
139 }
140}
141
142pub(crate) fn merge<N: Node>(
148 current: &mut N,
149 desired: &N,
150 ownership: &Ownership,
151) -> Result<(), MergeError> {
152 match ownership.claim(&[]) {
153 Claim::Owned => {
154 current.clone_from(desired);
155 Ok(())
156 }
157 Claim::Unmanaged => Ok(()),
158 Claim::Mixed => Merge { ownership }.container(current, Some(desired), &mut Vec::new()),
159 }
160}
161
162#[cfg(test)]
163mod tests;