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