1use crate::{
2 patch::{ListPatch, MapPatch, SetPatch},
3 traits::{Atomic, UpdateView},
4};
5use std::{
6 collections::{
7 BTreeMap, BTreeSet, HashMap, HashSet, btree_map::Entry as BTreeMapEntry,
8 hash_map::Entry as HashMapEntry,
9 },
10 hash::{BuildHasher, Hash},
11};
12use thiserror::Error as ThisError;
13
14#[derive(Clone, Debug, Eq, PartialEq, ThisError)]
21pub enum MergePatchError {
22 #[error("invalid patch shape: expected {expected}, found {actual}")]
23 InvalidShape {
24 expected: &'static str,
25 actual: &'static str,
26 },
27
28 #[error("invalid patch cardinality: expected {expected}, found {actual}")]
29 CardinalityViolation { expected: usize, actual: usize },
30
31 #[error("patch merge failed at {path}: {source}")]
32 Context {
33 path: String,
34 #[source]
35 source: Box<Self>,
36 },
37}
38
39impl MergePatchError {
40 #[must_use]
42 pub fn with_field(self, field: impl AsRef<str>) -> Self {
43 self.with_path_segment(field.as_ref())
44 }
45
46 #[must_use]
48 pub fn with_index(self, index: usize) -> Self {
49 self.with_path_segment(format!("[{index}]"))
50 }
51
52 #[must_use]
54 pub const fn path(&self) -> Option<&str> {
55 match self {
56 Self::Context { path, .. } => Some(path.as_str()),
57 _ => None,
58 }
59 }
60
61 #[must_use]
63 pub fn leaf(&self) -> &Self {
64 match self {
65 Self::Context { source, .. } => source.leaf(),
66 _ => self,
67 }
68 }
69
70 #[must_use]
71 fn with_path_segment(self, segment: impl Into<String>) -> Self {
72 let segment = segment.into();
73 match self {
74 Self::Context { path, source } => Self::Context {
75 path: Self::join_segments(segment.as_str(), path.as_str()),
76 source,
77 },
78 source => Self::Context {
79 path: segment,
80 source: Box::new(source),
81 },
82 }
83 }
84
85 #[must_use]
86 fn join_segments(prefix: &str, suffix: &str) -> String {
87 if suffix.starts_with('[') {
88 format!("{prefix}{suffix}")
89 } else {
90 format!("{prefix}.{suffix}")
91 }
92 }
93}
94
95pub fn merge_atomic<T>(value: &mut T, patch: T) -> Result<(), MergePatchError>
97where
98 T: Atomic + UpdateView<UpdateViewType = T>,
99{
100 *value = patch;
101
102 Ok(())
103}
104
105pub fn merge_option<T>(
107 value: &mut Option<T>,
108 patch: Option<T::UpdateViewType>,
109) -> Result<(), MergePatchError>
110where
111 T: UpdateView + Default,
112{
113 match patch {
114 None => {
115 *value = None;
117 }
118 Some(inner_patch) => {
119 if let Some(inner_value) = value.as_mut() {
120 inner_value
121 .merge(inner_patch)
122 .map_err(|err| err.with_field("value"))?;
123 } else {
124 let mut new_value = T::default();
125 new_value
126 .merge(inner_patch)
127 .map_err(|err| err.with_field("value"))?;
128 *value = Some(new_value);
129 }
130 }
131 }
132
133 Ok(())
134}
135
136pub fn merge_vec<T>(
138 values: &mut Vec<T>,
139 patches: Vec<ListPatch<T::UpdateViewType>>,
140) -> Result<(), MergePatchError>
141where
142 T: UpdateView + Default,
143{
144 for patch in patches {
145 match patch {
146 ListPatch::Update { index, patch } => {
147 if let Some(elem) = values.get_mut(index) {
148 elem.merge(patch).map_err(|err| err.with_index(index))?;
149 }
150 }
151
152 ListPatch::Insert { index, value } => {
153 let idx = index.min(values.len());
154 let mut elem = T::default();
155 elem.merge(value).map_err(|err| err.with_index(idx))?;
156 values.insert(idx, elem);
157 }
158
159 ListPatch::Push { value } => {
160 let idx = values.len();
161 let mut elem = T::default();
162 elem.merge(value).map_err(|err| err.with_index(idx))?;
163 values.push(elem);
164 }
165
166 ListPatch::Overwrite {
167 values: next_values,
168 } => {
169 values.clear();
170 values.reserve(next_values.len());
171
172 for (index, value) in next_values.into_iter().enumerate() {
173 let mut elem = T::default();
174 elem.merge(value).map_err(|err| err.with_index(index))?;
175 values.push(elem);
176 }
177 }
178
179 ListPatch::Remove { index } => {
180 if index < values.len() {
181 values.remove(index);
182 }
183 }
184
185 ListPatch::Clear => values.clear(),
186 }
187 }
188
189 Ok(())
190}
191
192pub fn merge_hash_set<T, S>(
194 values: &mut HashSet<T, S>,
195 patches: Vec<SetPatch<T::UpdateViewType>>,
196) -> Result<(), MergePatchError>
197where
198 T: UpdateView + Clone + Default + Eq + Hash,
199 S: BuildHasher + Default,
200{
201 for patch in patches {
202 match patch {
203 SetPatch::Insert(value) => {
204 let mut elem = T::default();
205 elem.merge(value).map_err(|err| err.with_field("insert"))?;
206 values.insert(elem);
207 }
208
209 SetPatch::Remove(value) => {
210 let mut elem = T::default();
211 elem.merge(value).map_err(|err| err.with_field("remove"))?;
212 values.remove(&elem);
213 }
214
215 SetPatch::Overwrite {
216 values: next_values,
217 } => {
218 values.clear();
219
220 for (index, value) in next_values.into_iter().enumerate() {
221 let mut elem = T::default();
222 elem.merge(value)
223 .map_err(|err| err.with_field("overwrite").with_index(index))?;
224 values.insert(elem);
225 }
226 }
227
228 SetPatch::Clear => values.clear(),
229 }
230 }
231
232 Ok(())
233}
234
235enum MapPatchOp<K, V> {
237 Insert { key: K, value: V },
238 Remove { key: K },
239 Replace { key: K, value: V },
240 Clear,
241}
242
243#[expect(clippy::too_many_lines)]
245pub fn merge_hash_map<K, V, S>(
246 values: &mut HashMap<K, V, S>,
247 patches: Vec<MapPatch<K::UpdateViewType, V::UpdateViewType>>,
248) -> Result<(), MergePatchError>
249where
250 K: UpdateView + Clone + Default + Eq + Hash,
251 V: UpdateView + Default,
252 S: BuildHasher + Default,
253{
254 let mut ops = Vec::with_capacity(patches.len());
256 for patch in patches {
257 match patch {
258 MapPatch::Insert { key, value } => {
259 let mut key_value = K::default();
260 key_value
261 .merge(key)
262 .map_err(|err| err.with_field("insert").with_field("key"))?;
263 ops.push(MapPatchOp::Insert {
264 key: key_value,
265 value,
266 });
267 }
268 MapPatch::Remove { key } => {
269 let mut key_value = K::default();
270 key_value
271 .merge(key)
272 .map_err(|err| err.with_field("remove").with_field("key"))?;
273 ops.push(MapPatchOp::Remove { key: key_value });
274 }
275 MapPatch::Replace { key, value } => {
276 let mut key_value = K::default();
277 key_value
278 .merge(key)
279 .map_err(|err| err.with_field("replace").with_field("key"))?;
280 ops.push(MapPatchOp::Replace {
281 key: key_value,
282 value,
283 });
284 }
285 MapPatch::Clear => ops.push(MapPatchOp::Clear),
286 }
287 }
288
289 let mut saw_clear = false;
291 let mut touched = HashSet::with_capacity(ops.len());
292 for op in &ops {
293 match op {
294 MapPatchOp::Clear => {
295 if saw_clear {
296 return Err(MergePatchError::InvalidShape {
297 expected: "at most one Clear operation per map patch batch",
298 actual: "duplicate Clear operations",
299 });
300 }
301 saw_clear = true;
302 if ops.len() != 1 {
303 return Err(MergePatchError::CardinalityViolation {
304 expected: 1,
305 actual: ops.len(),
306 });
307 }
308 }
309 MapPatchOp::Insert { key, .. }
310 | MapPatchOp::Remove { key }
311 | MapPatchOp::Replace { key, .. } => {
312 if saw_clear {
313 return Err(MergePatchError::InvalidShape {
314 expected: "Clear must be the only operation in a map patch batch",
315 actual: "Clear combined with key operation",
316 });
317 }
318 if !touched.insert(key.clone()) {
319 return Err(MergePatchError::InvalidShape {
320 expected: "unique key operations per map patch batch",
321 actual: "duplicate key operation",
322 });
323 }
324 }
325 }
326 }
327
328 if saw_clear {
329 values.clear();
330 return Ok(());
331 }
332
333 for op in ops {
335 match op {
336 MapPatchOp::Insert { key, value } => match values.entry(key) {
337 HashMapEntry::Occupied(mut slot) => {
338 slot.get_mut()
339 .merge(value)
340 .map_err(|err| err.with_field("insert").with_field("value"))?;
341 }
342 HashMapEntry::Vacant(slot) => {
343 let mut value_value = V::default();
344 value_value
345 .merge(value)
346 .map_err(|err| err.with_field("insert").with_field("value"))?;
347 slot.insert(value_value);
348 }
349 },
350
351 MapPatchOp::Remove { key } => {
352 values.remove(&key);
354 }
355
356 MapPatchOp::Replace { key, value } => match values.entry(key) {
357 HashMapEntry::Occupied(mut slot) => {
358 slot.get_mut()
359 .merge(value)
360 .map_err(|err| err.with_field("replace").with_field("value"))?;
361 }
362 HashMapEntry::Vacant(_) => {}
364 },
365
366 MapPatchOp::Clear => {
367 return Err(MergePatchError::InvalidShape {
368 expected: "Clear to be handled before apply phase",
369 actual: "Clear reached apply phase",
370 });
371 }
372 }
373 }
374
375 Ok(())
376}
377
378pub fn merge_btree_set<T>(
380 values: &mut BTreeSet<T>,
381 patches: Vec<SetPatch<T::UpdateViewType>>,
382) -> Result<(), MergePatchError>
383where
384 T: UpdateView + Clone + Default + Ord,
385{
386 for patch in patches {
387 match patch {
388 SetPatch::Insert(value) => {
389 let mut elem = T::default();
390 elem.merge(value).map_err(|err| err.with_field("insert"))?;
391 values.insert(elem);
392 }
393 SetPatch::Remove(value) => {
394 let mut elem = T::default();
395 elem.merge(value).map_err(|err| err.with_field("remove"))?;
396 values.remove(&elem);
397 }
398 SetPatch::Overwrite {
399 values: next_values,
400 } => {
401 values.clear();
402
403 for (index, value) in next_values.into_iter().enumerate() {
404 let mut elem = T::default();
405 elem.merge(value)
406 .map_err(|err| err.with_field("overwrite").with_index(index))?;
407 values.insert(elem);
408 }
409 }
410 SetPatch::Clear => values.clear(),
411 }
412 }
413
414 Ok(())
415}
416
417#[expect(clippy::too_many_lines)]
419pub fn merge_btree_map<K, V>(
420 values: &mut BTreeMap<K, V>,
421 patches: Vec<MapPatch<K::UpdateViewType, V::UpdateViewType>>,
422) -> Result<(), MergePatchError>
423where
424 K: UpdateView + Clone + Default + Ord,
425 V: UpdateView + Default,
426{
427 let mut ops = Vec::with_capacity(patches.len());
429 for patch in patches {
430 match patch {
431 MapPatch::Insert { key, value } => {
432 let mut key_value = K::default();
433 key_value
434 .merge(key)
435 .map_err(|err| err.with_field("insert").with_field("key"))?;
436 ops.push(MapPatchOp::Insert {
437 key: key_value,
438 value,
439 });
440 }
441 MapPatch::Remove { key } => {
442 let mut key_value = K::default();
443 key_value
444 .merge(key)
445 .map_err(|err| err.with_field("remove").with_field("key"))?;
446 ops.push(MapPatchOp::Remove { key: key_value });
447 }
448 MapPatch::Replace { key, value } => {
449 let mut key_value = K::default();
450 key_value
451 .merge(key)
452 .map_err(|err| err.with_field("replace").with_field("key"))?;
453 ops.push(MapPatchOp::Replace {
454 key: key_value,
455 value,
456 });
457 }
458 MapPatch::Clear => ops.push(MapPatchOp::Clear),
459 }
460 }
461
462 let mut saw_clear = false;
464 let mut touched = BTreeSet::new();
465 for op in &ops {
466 match op {
467 MapPatchOp::Clear => {
468 if saw_clear {
469 return Err(MergePatchError::InvalidShape {
470 expected: "at most one Clear operation per map patch batch",
471 actual: "duplicate Clear operations",
472 });
473 }
474 saw_clear = true;
475 if ops.len() != 1 {
476 return Err(MergePatchError::CardinalityViolation {
477 expected: 1,
478 actual: ops.len(),
479 });
480 }
481 }
482 MapPatchOp::Insert { key, .. }
483 | MapPatchOp::Remove { key }
484 | MapPatchOp::Replace { key, .. } => {
485 if saw_clear {
486 return Err(MergePatchError::InvalidShape {
487 expected: "Clear must be the only operation in a map patch batch",
488 actual: "Clear combined with key operation",
489 });
490 }
491 if !touched.insert(key.clone()) {
492 return Err(MergePatchError::InvalidShape {
493 expected: "unique key operations per map patch batch",
494 actual: "duplicate key operation",
495 });
496 }
497 }
498 }
499 }
500 if saw_clear {
501 values.clear();
502 return Ok(());
503 }
504
505 for op in ops {
507 match op {
508 MapPatchOp::Insert { key, value } => match values.entry(key) {
509 BTreeMapEntry::Occupied(mut slot) => {
510 slot.get_mut()
511 .merge(value)
512 .map_err(|err| err.with_field("insert").with_field("value"))?;
513 }
514 BTreeMapEntry::Vacant(slot) => {
515 let mut value_value = V::default();
516 value_value
517 .merge(value)
518 .map_err(|err| err.with_field("insert").with_field("value"))?;
519 slot.insert(value_value);
520 }
521 },
522 MapPatchOp::Remove { key } => {
523 values.remove(&key);
525 }
526 MapPatchOp::Replace { key, value } => match values.entry(key) {
527 BTreeMapEntry::Occupied(mut slot) => {
528 slot.get_mut()
529 .merge(value)
530 .map_err(|err| err.with_field("replace").with_field("value"))?;
531 }
532 BTreeMapEntry::Vacant(_) => {}
534 },
535 MapPatchOp::Clear => {
536 return Err(MergePatchError::InvalidShape {
537 expected: "Clear to be handled before apply phase",
538 actual: "Clear reached apply phase",
539 });
540 }
541 }
542 }
543
544 Ok(())
545}