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
14pub trait MergePatch: UpdateView {
19 fn merge(&mut self, patch: Self::UpdateViewType) -> Result<(), MergePatchError>;
20}
21
22impl<T> MergePatch for T
23where
24 T: Atomic + UpdateView<UpdateViewType = T>,
25{
26 fn merge(&mut self, patch: Self::UpdateViewType) -> Result<(), MergePatchError> {
27 *self = patch;
28
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 fn merge(&mut self, patch: Self::UpdateViewType) -> Result<(), MergePatchError> {
122 match patch {
123 None => {
124 *self = None;
126 }
127 Some(inner_patch) => {
128 if let Some(inner_value) = self.as_mut() {
129 inner_value
130 .merge(inner_patch)
131 .map_err(|err| err.with_field("value"))?;
132 } else {
133 let mut new_value = T::default();
134 new_value
135 .merge(inner_patch)
136 .map_err(|err| err.with_field("value"))?;
137 *self = Some(new_value);
138 }
139 }
140 }
141
142 Ok(())
143 }
144}
145
146impl<T> MergePatch for Vec<T>
147where
148 T: MergePatch + Default,
149{
150 fn merge(&mut self, patches: Self::UpdateViewType) -> Result<(), MergePatchError> {
151 for patch in patches {
152 match patch {
153 ListPatch::Update { index, patch } => {
154 if let Some(elem) = self.get_mut(index) {
155 elem.merge(patch).map_err(|err| err.with_index(index))?;
156 }
157 }
158
159 ListPatch::Insert { index, value } => {
160 let idx = index.min(self.len());
161 let mut elem = T::default();
162 elem.merge(value).map_err(|err| err.with_index(idx))?;
163 self.insert(idx, elem);
164 }
165
166 ListPatch::Push { value } => {
167 let idx = self.len();
168 let mut elem = T::default();
169 elem.merge(value).map_err(|err| err.with_index(idx))?;
170 self.push(elem);
171 }
172
173 ListPatch::Overwrite { values } => {
174 self.clear();
175 self.reserve(values.len());
176
177 for (index, value) in values.into_iter().enumerate() {
178 let mut elem = T::default();
179 elem.merge(value).map_err(|err| err.with_index(index))?;
180 self.push(elem);
181 }
182 }
183
184 ListPatch::Remove { index } => {
185 if index < self.len() {
186 self.remove(index);
187 }
188 }
189
190 ListPatch::Clear => self.clear(),
191 }
192 }
193
194 Ok(())
195 }
196}
197
198impl<T, S> MergePatch for HashSet<T, S>
199where
200 T: MergePatch + Clone + Default + Eq + Hash,
201 S: BuildHasher + Default,
202{
203 fn merge(&mut self, patches: Self::UpdateViewType) -> Result<(), MergePatchError> {
204 for patch in patches {
205 match patch {
206 SetPatch::Insert(value) => {
207 let mut elem = T::default();
208 elem.merge(value).map_err(|err| err.with_field("insert"))?;
209 self.insert(elem);
210 }
211
212 SetPatch::Remove(value) => {
213 let mut elem = T::default();
214 elem.merge(value).map_err(|err| err.with_field("remove"))?;
215 self.remove(&elem);
216 }
217
218 SetPatch::Overwrite { values } => {
219 self.clear();
220
221 for (index, value) in values.into_iter().enumerate() {
222 let mut elem = T::default();
223 elem.merge(value)
224 .map_err(|err| err.with_field("overwrite").with_index(index))?;
225 self.insert(elem);
226 }
227 }
228
229 SetPatch::Clear => self.clear(),
230 }
231 }
232
233 Ok(())
234 }
235}
236
237enum MapPatchOp<K, V> {
239 Insert { key: K, value: V },
240 Remove { key: K },
241 Replace { key: K, value: V },
242 Clear,
243}
244
245impl<K, V, S> MergePatch for HashMap<K, V, S>
246where
247 K: MergePatch + Clone + Default + Eq + Hash,
248 V: MergePatch + Default,
249 S: BuildHasher + Default,
250{
251 #[expect(clippy::too_many_lines)]
252 fn merge(&mut self, patches: Self::UpdateViewType) -> Result<(), MergePatchError> {
253 let mut ops = Vec::with_capacity(patches.len());
255 for patch in patches {
256 match patch {
257 MapPatch::Insert { key, value } => {
258 let mut key_value = K::default();
259 key_value
260 .merge(key)
261 .map_err(|err| err.with_field("insert").with_field("key"))?;
262 ops.push(MapPatchOp::Insert {
263 key: key_value,
264 value,
265 });
266 }
267 MapPatch::Remove { key } => {
268 let mut key_value = K::default();
269 key_value
270 .merge(key)
271 .map_err(|err| err.with_field("remove").with_field("key"))?;
272 ops.push(MapPatchOp::Remove { key: key_value });
273 }
274 MapPatch::Replace { key, value } => {
275 let mut key_value = K::default();
276 key_value
277 .merge(key)
278 .map_err(|err| err.with_field("replace").with_field("key"))?;
279 ops.push(MapPatchOp::Replace {
280 key: key_value,
281 value,
282 });
283 }
284 MapPatch::Clear => ops.push(MapPatchOp::Clear),
285 }
286 }
287
288 let mut saw_clear = false;
290 let mut touched = HashSet::with_capacity(ops.len());
291 for op in &ops {
292 match op {
293 MapPatchOp::Clear => {
294 if saw_clear {
295 return Err(MergePatchError::InvalidShape {
296 expected: "at most one Clear operation per map patch batch",
297 actual: "duplicate Clear operations",
298 });
299 }
300 saw_clear = true;
301 if ops.len() != 1 {
302 return Err(MergePatchError::CardinalityViolation {
303 expected: 1,
304 actual: ops.len(),
305 });
306 }
307 }
308 MapPatchOp::Insert { key, .. }
309 | MapPatchOp::Remove { key }
310 | MapPatchOp::Replace { key, .. } => {
311 if saw_clear {
312 return Err(MergePatchError::InvalidShape {
313 expected: "Clear must be the only operation in a map patch batch",
314 actual: "Clear combined with key operation",
315 });
316 }
317 if !touched.insert(key.clone()) {
318 return Err(MergePatchError::InvalidShape {
319 expected: "unique key operations per map patch batch",
320 actual: "duplicate key operation",
321 });
322 }
323 }
324 }
325 }
326
327 if saw_clear {
328 self.clear();
329 return Ok(());
330 }
331
332 for op in ops {
334 match op {
335 MapPatchOp::Insert { key, value } => match self.entry(key) {
336 HashMapEntry::Occupied(mut slot) => {
337 slot.get_mut()
338 .merge(value)
339 .map_err(|err| err.with_field("insert").with_field("value"))?;
340 }
341 HashMapEntry::Vacant(slot) => {
342 let mut value_value = V::default();
343 value_value
344 .merge(value)
345 .map_err(|err| err.with_field("insert").with_field("value"))?;
346 slot.insert(value_value);
347 }
348 },
349
350 MapPatchOp::Remove { key } => {
351 if self.remove(&key).is_none() {
352 return Err(MergePatchError::MissingKey {
353 operation: "remove",
354 });
355 }
356 }
357
358 MapPatchOp::Replace { key, value } => match self.entry(key) {
359 HashMapEntry::Occupied(mut slot) => {
360 slot.get_mut()
361 .merge(value)
362 .map_err(|err| err.with_field("replace").with_field("value"))?;
363 }
364 HashMapEntry::Vacant(_) => {
365 return Err(MergePatchError::MissingKey {
366 operation: "replace",
367 });
368 }
369 },
370
371 MapPatchOp::Clear => {
372 return Err(MergePatchError::InvalidShape {
373 expected: "Clear to be handled before apply phase",
374 actual: "Clear reached apply phase",
375 });
376 }
377 }
378 }
379
380 Ok(())
381 }
382}
383
384impl<T> MergePatch for BTreeSet<T>
385where
386 T: MergePatch + Clone + Default + Ord,
387{
388 fn merge(&mut self, patches: Self::UpdateViewType) -> Result<(), MergePatchError> {
389 for patch in patches {
390 match patch {
391 SetPatch::Insert(value) => {
392 let mut elem = T::default();
393 elem.merge(value).map_err(|err| err.with_field("insert"))?;
394 self.insert(elem);
395 }
396 SetPatch::Remove(value) => {
397 let mut elem = T::default();
398 elem.merge(value).map_err(|err| err.with_field("remove"))?;
399 self.remove(&elem);
400 }
401 SetPatch::Overwrite { values } => {
402 self.clear();
403
404 for (index, value) in values.into_iter().enumerate() {
405 let mut elem = T::default();
406 elem.merge(value)
407 .map_err(|err| err.with_field("overwrite").with_index(index))?;
408 self.insert(elem);
409 }
410 }
411 SetPatch::Clear => self.clear(),
412 }
413 }
414
415 Ok(())
416 }
417}
418
419impl<K, V> MergePatch for BTreeMap<K, V>
420where
421 K: MergePatch + Clone + Default + Ord,
422 V: MergePatch + Default,
423{
424 #[expect(clippy::too_many_lines)]
425 fn merge(&mut self, patches: Self::UpdateViewType) -> Result<(), MergePatchError> {
426 let mut ops = Vec::with_capacity(patches.len());
428 for patch in patches {
429 match patch {
430 MapPatch::Insert { key, value } => {
431 let mut key_value = K::default();
432 key_value
433 .merge(key)
434 .map_err(|err| err.with_field("insert").with_field("key"))?;
435 ops.push(MapPatchOp::Insert {
436 key: key_value,
437 value,
438 });
439 }
440 MapPatch::Remove { key } => {
441 let mut key_value = K::default();
442 key_value
443 .merge(key)
444 .map_err(|err| err.with_field("remove").with_field("key"))?;
445 ops.push(MapPatchOp::Remove { key: key_value });
446 }
447 MapPatch::Replace { key, value } => {
448 let mut key_value = K::default();
449 key_value
450 .merge(key)
451 .map_err(|err| err.with_field("replace").with_field("key"))?;
452 ops.push(MapPatchOp::Replace {
453 key: key_value,
454 value,
455 });
456 }
457 MapPatch::Clear => ops.push(MapPatchOp::Clear),
458 }
459 }
460
461 let mut saw_clear = false;
463 let mut touched = BTreeSet::new();
464 for op in &ops {
465 match op {
466 MapPatchOp::Clear => {
467 if saw_clear {
468 return Err(MergePatchError::InvalidShape {
469 expected: "at most one Clear operation per map patch batch",
470 actual: "duplicate Clear operations",
471 });
472 }
473 saw_clear = true;
474 if ops.len() != 1 {
475 return Err(MergePatchError::CardinalityViolation {
476 expected: 1,
477 actual: ops.len(),
478 });
479 }
480 }
481 MapPatchOp::Insert { key, .. }
482 | MapPatchOp::Remove { key }
483 | MapPatchOp::Replace { key, .. } => {
484 if saw_clear {
485 return Err(MergePatchError::InvalidShape {
486 expected: "Clear must be the only operation in a map patch batch",
487 actual: "Clear combined with key operation",
488 });
489 }
490 if !touched.insert(key.clone()) {
491 return Err(MergePatchError::InvalidShape {
492 expected: "unique key operations per map patch batch",
493 actual: "duplicate key operation",
494 });
495 }
496 }
497 }
498 }
499 if saw_clear {
500 self.clear();
501 return Ok(());
502 }
503
504 for op in ops {
506 match op {
507 MapPatchOp::Insert { key, value } => match self.entry(key) {
508 BTreeMapEntry::Occupied(mut slot) => {
509 slot.get_mut()
510 .merge(value)
511 .map_err(|err| err.with_field("insert").with_field("value"))?;
512 }
513 BTreeMapEntry::Vacant(slot) => {
514 let mut value_value = V::default();
515 value_value
516 .merge(value)
517 .map_err(|err| err.with_field("insert").with_field("value"))?;
518 slot.insert(value_value);
519 }
520 },
521 MapPatchOp::Remove { key } => {
522 if self.remove(&key).is_none() {
523 return Err(MergePatchError::MissingKey {
524 operation: "remove",
525 });
526 }
527 }
528 MapPatchOp::Replace { key, value } => match self.entry(key) {
529 BTreeMapEntry::Occupied(mut slot) => {
530 slot.get_mut()
531 .merge(value)
532 .map_err(|err| err.with_field("replace").with_field("value"))?;
533 }
534 BTreeMapEntry::Vacant(_) => {
535 return Err(MergePatchError::MissingKey {
536 operation: "replace",
537 });
538 }
539 },
540 MapPatchOp::Clear => {
541 return Err(MergePatchError::InvalidShape {
542 expected: "Clear to be handled before apply phase",
543 actual: "Clear reached apply phase",
544 });
545 }
546 }
547 }
548
549 Ok(())
550 }
551}