use std::collections::BTreeSet;
use std::sync::Arc;
use crate::value::ops::LenIterWrap;
use crate::value::{Enumerator, Object, ObjectExt, ObjectRepr, Value, ValueKind};
#[derive(Debug)]
pub struct MergeDict {
values: Box<[Value]>,
}
impl MergeDict {
pub fn new(values: Vec<Value>) -> Self {
Self {
values: values.into_boxed_slice(),
}
}
}
impl Object for MergeDict {
fn get_value(self: &Arc<Self>, key: &Value) -> Option<Value> {
for value in self.values.iter().rev() {
if let Ok(v) = value.get_item(key) {
if !v.is_undefined() {
return Some(v);
}
}
}
None
}
fn enumerate(self: &Arc<Self>) -> Enumerator {
let keys: BTreeSet<Value> = self
.values
.iter()
.filter(|x| x.kind() == ValueKind::Map)
.filter_map(|v| v.try_iter().ok())
.flatten()
.collect();
Enumerator::Iter(Box::new(keys.into_iter()))
}
}
#[derive(Debug)]
pub struct MergeSeq {
values: Box<[Value]>,
total_len: Option<usize>,
}
impl MergeSeq {
pub fn new(values: Vec<Value>) -> Self {
Self {
total_len: values.iter().map(|v| v.len()).sum(),
values: values.into_boxed_slice(),
}
}
}
impl Object for MergeSeq {
fn repr(self: &Arc<Self>) -> ObjectRepr {
ObjectRepr::Seq
}
fn get_value(self: &Arc<Self>, key: &Value) -> Option<Value> {
if let Some(idx) = key.as_usize() {
let mut current_idx = 0;
for value in self.values.iter() {
let len = value.len().unwrap_or(0);
if idx < current_idx + len {
return value.get_item(&Value::from(idx - current_idx)).ok();
}
current_idx += len;
}
}
None
}
fn enumerate(self: &Arc<Self>) -> Enumerator {
self.mapped_enumerator(|this| {
let iter = this.values.iter().flat_map(|v| match v.try_iter() {
Ok(iter) => Box::new(iter) as Box<dyn Iterator<Item = Value> + Send + Sync>,
Err(err) => Box::new(Some(Value::from(err)).into_iter())
as Box<dyn Iterator<Item = Value> + Send + Sync>,
});
if let Some(total_len) = this.total_len {
Box::new(LenIterWrap(total_len, iter))
} else {
Box::new(iter)
}
})
}
}
pub fn merge_maps<I, V>(iter: I) -> Value
where
I: IntoIterator<Item = V>,
V: Into<Value>,
{
let sources: Vec<Value> = iter.into_iter().map(Into::into).collect();
if sources.len() == 1 {
sources[0].clone()
} else {
Value::from_object(MergeDict::new(sources))
}
}
#[test]
fn test_merge_object() {
use std::collections::BTreeMap;
let o = merge_maps([Value::from("abc"), Value::from(vec![1, 2, 3])]);
assert_eq!(o, Value::from(BTreeMap::<String, String>::new()));
let mut map1 = BTreeMap::new();
map1.insert("a", 1);
map1.insert("b", 2);
let mut map2 = BTreeMap::new();
map2.insert("b", 3);
map2.insert("c", 4);
let merged = merge_maps([Value::from(map1), Value::from(map2)]);
assert_eq!(merged.get_attr("a").unwrap(), Value::from(1));
assert_eq!(merged.get_attr("b").unwrap(), Value::from(3)); assert_eq!(merged.get_attr("c").unwrap(), Value::from(4));
}