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