diff --git a/implementations/rust/preserves/src/value/merge.rs b/implementations/rust/preserves/src/value/merge.rs new file mode 100644 index 0000000..8e5a3dd --- /dev/null +++ b/implementations/rust/preserves/src/value/merge.rs @@ -0,0 +1,62 @@ +use super::Embeddable; +use super::Map; +use super::NestedValue; +use super::Record; +use super::Value; + +pub fn merge_seqs, D: Embeddable>(mut a: Vec, mut b: Vec) -> Option> { + if a.len() > b.len() { + std::mem::swap(&mut a, &mut b); + } + let mut r = vec![]; + let mut bi = b.into_iter(); + for av in a.into_iter() { + r.push(merge2(av, bi.next().unwrap())?); + } + r.extend(bi); + Some(r) +} + +pub fn merge2, D: Embeddable>(v: N, w: N) -> Option { + let (mut v_anns, v_val) = v.pieces(); + let (w_anns, w_val) = w.pieces(); + v_anns.modify(|anns| anns.extend(w_anns.to_vec().into_iter())); + if v_val == w_val { + Some(N::wrap(v_anns, v_val)) + } else { + let maybe_merged = match v_val { + Value::Record(rv) => + Some(Value::Record(Record(merge_seqs(rv.0, w_val.into_record()?.0)?))), + Value::Sequence(vs) => + Some(Value::Sequence(merge_seqs(vs, w_val.into_sequence()?)?)), + Value::Set(_vs) => + None, // unsure how to merge sets + Value::Dictionary(vs) => { + let mut ws = w_val.into_dictionary()?; + let mut rs = Map::new(); + for (k, vv) in vs.into_iter() { + match ws.remove(&k) { + Some(wv) => { rs.insert(k, merge2(vv, wv)?); } + None => { rs.insert(k, vv); } + } + } + rs.extend(ws.into_iter()); + Some(Value::Dictionary(rs)) + } + _ => None, + }; + maybe_merged.map(|vw| N::wrap(v_anns, vw)) + } +} + +pub fn merge, D: Embeddable, I: IntoIterator>(vs: I) -> Option { + let mut vs = vs.into_iter(); + let mut v = vs.next().expect("at least one value in merge()"); + for w in vs { + match merge2(v, w) { + Some(merged) => v = merged, + None => return None, + } + } + Some(v) +} diff --git a/implementations/rust/preserves/src/value/mod.rs b/implementations/rust/preserves/src/value/mod.rs index 04b7fe5..1ae56b4 100644 --- a/implementations/rust/preserves/src/value/mod.rs +++ b/implementations/rust/preserves/src/value/mod.rs @@ -10,6 +10,7 @@ pub mod signed_integer; pub mod suspendable; pub mod text; pub mod writer; +pub mod merge; pub use de::Deserializer; pub use de::from_value; @@ -19,6 +20,7 @@ pub use domain::DomainParse; pub use domain::IOValueDomainCodec; pub use domain::NoEmbeddedDomainCodec; pub use domain::ViaCodec; +pub use merge::merge; pub use packed::PackedReader; pub use packed::PackedWriter; pub use reader::BinarySource;