minijinja/value/
merge_object.rs1use std::collections::BTreeSet;
2use std::sync::Arc;
3
4use crate::value::ops::LenIterWrap;
5use crate::value::{Enumerator, Object, ObjectExt, ObjectRepr, Value, ValueKind};
6
7#[derive(Debug)]
9pub struct MergeDict {
10 values: Box<[Value]>,
11}
12
13impl MergeDict {
14 pub fn new(values: Vec<Value>) -> Self {
15 Self {
16 values: values.into_boxed_slice(),
17 }
18 }
19}
20
21impl Object for MergeDict {
22 fn get_value(self: &Arc<Self>, key: &Value) -> Option<Value> {
23 for value in self.values.iter().rev() {
25 if let Ok(v) = value.get_item(key) {
26 if !v.is_undefined() {
27 return Some(v);
28 }
29 }
30 }
31 None
32 }
33
34 fn enumerate(self: &Arc<Self>) -> Enumerator {
35 let keys: BTreeSet<Value> = self
37 .values
38 .iter()
39 .filter(|x| x.kind() == ValueKind::Map)
40 .filter_map(|v| v.try_iter().ok())
41 .flatten()
42 .collect();
43 Enumerator::Iter(Box::new(keys.into_iter()))
44 }
45}
46
47#[derive(Debug)]
49pub struct MergeSeq {
50 values: Box<[Value]>,
51 total_len: Option<usize>,
52}
53
54impl MergeSeq {
55 pub fn new(values: Vec<Value>) -> Self {
56 Self {
57 total_len: values.iter().map(|v| v.len()).sum(),
58 values: values.into_boxed_slice(),
59 }
60 }
61}
62
63impl Object for MergeSeq {
64 fn repr(self: &Arc<Self>) -> ObjectRepr {
65 ObjectRepr::Seq
66 }
67
68 fn get_value(self: &Arc<Self>, key: &Value) -> Option<Value> {
69 if let Some(idx) = key.as_usize() {
70 let mut current_idx = 0;
71 for value in self.values.iter() {
72 let len = value.len().unwrap_or(0);
73 if idx < current_idx + len {
74 return value.get_item(&Value::from(idx - current_idx)).ok();
75 }
76 current_idx += len;
77 }
78 }
79 None
80 }
81
82 fn enumerate(self: &Arc<Self>) -> Enumerator {
83 self.mapped_enumerator(|this| {
84 let iter = this.values.iter().flat_map(|v| match v.try_iter() {
85 Ok(iter) => Box::new(iter) as Box<dyn Iterator<Item = Value> + Send + Sync>,
86 Err(err) => Box::new(Some(Value::from(err)).into_iter())
87 as Box<dyn Iterator<Item = Value> + Send + Sync>,
88 });
89 if let Some(total_len) = this.total_len {
90 Box::new(LenIterWrap(total_len, iter))
91 } else {
92 Box::new(iter)
93 }
94 })
95 }
96}
97
98pub fn merge_maps<I, V>(iter: I) -> Value
126where
127 I: IntoIterator<Item = V>,
128 V: Into<Value>,
129{
130 let sources: Vec<Value> = iter.into_iter().map(Into::into).collect();
131 if sources.len() == 1 {
134 sources[0].clone()
135 } else {
136 Value::from_object(MergeDict::new(sources))
137 }
138}
139
140#[test]
141fn test_merge_object() {
142 use std::collections::BTreeMap;
143
144 let o = merge_maps([Value::from("abc"), Value::from(vec![1, 2, 3])]);
145 assert_eq!(o, Value::from(BTreeMap::<String, String>::new()));
146
147 let mut map1 = BTreeMap::new();
148 map1.insert("a", 1);
149 map1.insert("b", 2);
150
151 let mut map2 = BTreeMap::new();
152 map2.insert("b", 3);
153 map2.insert("c", 4);
154
155 let merged = merge_maps([Value::from(map1), Value::from(map2)]);
156
157 assert_eq!(merged.get_attr("a").unwrap(), Value::from(1));
160 assert_eq!(merged.get_attr("b").unwrap(), Value::from(3)); assert_eq!(merged.get_attr("c").unwrap(), Value::from(4));
162}