loose_liquid_core/model/object/
mod.rs

1//! Type representing a Liquid object, payload of the `Value::Object` variant
2
3pub mod map;
4mod ser;
5
6use std::collections::BTreeMap;
7use std::collections::HashMap;
8use std::fmt;
9
10use crate::model::KStringCow;
11
12use crate::model::value::DisplayCow;
13use crate::model::State;
14use crate::model::{Value, ValueView};
15
16pub use map::Object;
17pub use ser::to_object;
18
19/// Accessor for objects.
20pub trait ObjectView: ValueView {
21    /// Cast to ValueView
22    fn as_value(&self) -> &dyn ValueView;
23
24    /// Returns the number of elements.
25    fn size(&self) -> i64;
26
27    /// Keys available for lookup.
28    fn keys<'k>(&'k self) -> Box<dyn Iterator<Item = KStringCow<'k>> + 'k>;
29    /// Keys available for lookup.
30    fn values<'k>(&'k self) -> Box<dyn Iterator<Item = &'k dyn ValueView> + 'k>;
31    /// Returns an iterator .
32    fn iter<'k>(&'k self) -> Box<dyn Iterator<Item = (KStringCow<'k>, &'k dyn ValueView)> + 'k>;
33
34    /// Access a contained `BoxedValue`.
35    fn contains_key(&self, index: &str) -> bool;
36    /// Access a contained `Value`.
37    fn get<'s>(&'s self, index: &str) -> Option<&'s dyn ValueView>;
38}
39
40impl ValueView for Object {
41    fn as_debug(&self) -> &dyn fmt::Debug {
42        self
43    }
44
45    fn render(&self) -> DisplayCow<'_> {
46        DisplayCow::Owned(Box::new(ObjectRender { s: self }))
47    }
48    fn source(&self) -> DisplayCow<'_> {
49        DisplayCow::Owned(Box::new(ObjectSource { s: self }))
50    }
51    fn type_name(&self) -> &'static str {
52        "object"
53    }
54    fn query_state(&self, state: State) -> bool {
55        match state {
56            State::Truthy => true,
57            State::DefaultValue | State::Empty | State::Blank => self.is_empty(),
58        }
59    }
60
61    fn to_kstr(&self) -> KStringCow<'_> {
62        let s = ObjectRender { s: self }.to_string();
63        KStringCow::from_string(s)
64    }
65    fn to_value(&self) -> Value {
66        Value::Object(self.clone())
67    }
68
69    fn as_object(&self) -> Option<&dyn ObjectView> {
70        Some(self)
71    }
72}
73
74impl ObjectView for Object {
75    fn as_value(&self) -> &dyn ValueView {
76        self
77    }
78
79    fn size(&self) -> i64 {
80        self.len() as i64
81    }
82
83    fn keys<'k>(&'k self) -> Box<dyn Iterator<Item = KStringCow<'k>> + 'k> {
84        let keys = Object::keys(self).map(|s| s.as_ref().into());
85        Box::new(keys)
86    }
87
88    fn values<'k>(&'k self) -> Box<dyn Iterator<Item = &'k dyn ValueView> + 'k> {
89        let i = Object::values(self).map(|v| v.as_view());
90        Box::new(i)
91    }
92
93    fn iter<'k>(&'k self) -> Box<dyn Iterator<Item = (KStringCow<'k>, &'k dyn ValueView)> + 'k> {
94        let i = Object::iter(self).map(|(k, v)| (k.as_str().into(), v.as_view()));
95        Box::new(i)
96    }
97
98    fn contains_key(&self, index: &str) -> bool {
99        Object::contains_key(self, index)
100    }
101
102    fn get<'s>(&'s self, index: &str) -> Option<&'s dyn ValueView> {
103        Object::get(self, index).map(|v| v.as_view())
104    }
105}
106
107impl<'o, O: ObjectView + ?Sized> ObjectView for &'o O {
108    fn as_value(&self) -> &dyn ValueView {
109        <O as ObjectView>::as_value(self)
110    }
111
112    fn size(&self) -> i64 {
113        <O as ObjectView>::size(self)
114    }
115
116    fn keys<'k>(&'k self) -> Box<dyn Iterator<Item = KStringCow<'k>> + 'k> {
117        <O as ObjectView>::keys(self)
118    }
119
120    fn values<'k>(&'k self) -> Box<dyn Iterator<Item = &'k dyn ValueView> + 'k> {
121        <O as ObjectView>::values(self)
122    }
123
124    fn iter<'k>(&'k self) -> Box<dyn Iterator<Item = (KStringCow<'k>, &'k dyn ValueView)> + 'k> {
125        <O as ObjectView>::iter(self)
126    }
127
128    fn contains_key(&self, index: &str) -> bool {
129        <O as ObjectView>::contains_key(self, index)
130    }
131
132    fn get<'s>(&'s self, index: &str) -> Option<&'s dyn ValueView> {
133        <O as ObjectView>::get(self, index)
134    }
135}
136
137/// Owned object index
138pub trait ObjectIndex:
139    fmt::Debug + fmt::Display + Ord + std::hash::Hash + Eq + std::borrow::Borrow<str>
140{
141    /// Borrow the index
142    fn as_index(&self) -> &str;
143}
144
145impl ObjectIndex for String {
146    fn as_index(&self) -> &str {
147        self.as_str()
148    }
149}
150
151impl ObjectIndex for crate::model::KString {
152    fn as_index(&self) -> &str {
153        self.as_str()
154    }
155}
156
157impl<'s> ObjectIndex for crate::model::KStringRef<'s> {
158    fn as_index(&self) -> &str {
159        self.as_str()
160    }
161}
162
163impl<'s> ObjectIndex for crate::model::KStringCow<'s> {
164    fn as_index(&self) -> &str {
165        self.as_str()
166    }
167}
168
169impl<K: ObjectIndex, V: ValueView, S: ::std::hash::BuildHasher> ValueView for HashMap<K, V, S> {
170    fn as_debug(&self) -> &dyn fmt::Debug {
171        self
172    }
173
174    fn render(&self) -> DisplayCow<'_> {
175        DisplayCow::Owned(Box::new(ObjectRender { s: self }))
176    }
177    fn source(&self) -> DisplayCow<'_> {
178        DisplayCow::Owned(Box::new(ObjectSource { s: self }))
179    }
180    fn type_name(&self) -> &'static str {
181        "object"
182    }
183    fn query_state(&self, state: State) -> bool {
184        match state {
185            State::Truthy => true,
186            State::DefaultValue | State::Empty | State::Blank => self.is_empty(),
187        }
188    }
189
190    fn to_kstr(&self) -> KStringCow<'_> {
191        let s = ObjectRender { s: self }.to_string();
192        KStringCow::from_string(s)
193    }
194    fn to_value(&self) -> Value {
195        Value::Object(
196            self.iter()
197                .map(|(k, v)| (crate::model::KString::from_ref(k.as_index()), v.to_value()))
198                .collect(),
199        )
200    }
201
202    fn as_object(&self) -> Option<&dyn ObjectView> {
203        Some(self)
204    }
205}
206
207impl<K: ObjectIndex, V: ValueView, S: ::std::hash::BuildHasher> ObjectView for HashMap<K, V, S> {
208    fn as_value(&self) -> &dyn ValueView {
209        self
210    }
211
212    fn size(&self) -> i64 {
213        self.len() as i64
214    }
215
216    fn keys<'k>(&'k self) -> Box<dyn Iterator<Item = KStringCow<'k>> + 'k> {
217        let keys = HashMap::keys(self).map(|s| s.as_index().into());
218        Box::new(keys)
219    }
220
221    fn values<'k>(&'k self) -> Box<dyn Iterator<Item = &'k dyn ValueView> + 'k> {
222        let i = HashMap::values(self).map(as_view);
223        Box::new(i)
224    }
225
226    fn iter<'k>(&'k self) -> Box<dyn Iterator<Item = (KStringCow<'k>, &'k dyn ValueView)> + 'k> {
227        let i = HashMap::iter(self).map(|(k, v)| (k.as_index().into(), as_view(v)));
228        Box::new(i)
229    }
230
231    fn contains_key(&self, index: &str) -> bool {
232        HashMap::contains_key(self, index)
233    }
234
235    fn get<'s>(&'s self, index: &str) -> Option<&'s dyn ValueView> {
236        HashMap::get(self, index).map(as_view)
237    }
238}
239
240impl<K: ObjectIndex, V: ValueView> ValueView for BTreeMap<K, V> {
241    fn as_debug(&self) -> &dyn fmt::Debug {
242        self
243    }
244
245    fn render(&self) -> DisplayCow<'_> {
246        DisplayCow::Owned(Box::new(ObjectRender { s: self }))
247    }
248    fn source(&self) -> DisplayCow<'_> {
249        DisplayCow::Owned(Box::new(ObjectSource { s: self }))
250    }
251    fn type_name(&self) -> &'static str {
252        "object"
253    }
254    fn query_state(&self, state: State) -> bool {
255        match state {
256            State::Truthy => true,
257            State::DefaultValue | State::Empty | State::Blank => self.is_empty(),
258        }
259    }
260
261    fn to_kstr(&self) -> KStringCow<'_> {
262        let s = ObjectRender { s: self }.to_string();
263        KStringCow::from_string(s)
264    }
265    fn to_value(&self) -> Value {
266        Value::Object(
267            self.iter()
268                .map(|(k, v)| (crate::model::KString::from_ref(k.as_index()), v.to_value()))
269                .collect(),
270        )
271    }
272
273    fn as_object(&self) -> Option<&dyn ObjectView> {
274        Some(self)
275    }
276}
277
278impl<K: ObjectIndex, V: ValueView> ObjectView for BTreeMap<K, V> {
279    fn as_value(&self) -> &dyn ValueView {
280        self
281    }
282
283    fn size(&self) -> i64 {
284        self.len() as i64
285    }
286
287    fn keys<'k>(&'k self) -> Box<dyn Iterator<Item = KStringCow<'k>> + 'k> {
288        let keys = BTreeMap::keys(self).map(|s| s.as_index().into());
289        Box::new(keys)
290    }
291
292    fn values<'k>(&'k self) -> Box<dyn Iterator<Item = &'k dyn ValueView> + 'k> {
293        let i = BTreeMap::values(self).map(as_view);
294        Box::new(i)
295    }
296
297    fn iter<'k>(&'k self) -> Box<dyn Iterator<Item = (KStringCow<'k>, &'k dyn ValueView)> + 'k> {
298        let i = BTreeMap::iter(self).map(|(k, v)| (k.as_index().into(), as_view(v)));
299        Box::new(i)
300    }
301
302    fn contains_key(&self, index: &str) -> bool {
303        BTreeMap::contains_key(self, index)
304    }
305
306    fn get<'s>(&'s self, index: &str) -> Option<&'s dyn ValueView> {
307        BTreeMap::get(self, index).map(as_view)
308    }
309}
310
311fn as_view<T: ValueView>(value: &T) -> &dyn ValueView {
312    value
313}
314
315#[derive(Debug)]
316/// Helper for `ObjectView::source`
317pub struct ObjectSource<'s, O: ObjectView> {
318    s: &'s O,
319}
320
321impl<'s, O: ObjectView> ObjectSource<'s, O> {
322    #[doc(hidden)]
323    pub fn new(other: &'s O) -> Self {
324        Self { s: other }
325    }
326}
327
328impl<'s, O: ObjectView> fmt::Display for ObjectSource<'s, O> {
329    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
330        write!(f, "{{")?;
331        for (k, v) in self.s.iter() {
332            write!(f, r#""{}": {}, "#, k, v.render())?;
333        }
334        write!(f, "}}")?;
335        Ok(())
336    }
337}
338
339#[derive(Debug)]
340/// Helper for `ObjectView::render`
341pub struct ObjectRender<'s, O: ObjectView> {
342    s: &'s O,
343}
344
345impl<'s, O: ObjectView> ObjectRender<'s, O> {
346    #[doc(hidden)]
347    pub fn new(other: &'s O) -> Self {
348        Self { s: other }
349    }
350}
351
352impl<'s, O: ObjectView> fmt::Display for ObjectRender<'s, O> {
353    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
354        for (k, v) in self.s.iter() {
355            write!(f, "{}{}", k, v.render())?;
356        }
357        Ok(())
358    }
359}
360
361#[cfg(test)]
362mod test {
363    use super::*;
364
365    #[test]
366    fn test_object() {
367        let obj = Object::new();
368        println!("{}", obj.source());
369        let object: &dyn ObjectView = &obj;
370        println!("{}", object.source());
371        let view: &dyn ValueView = object.as_value();
372        println!("{}", view.source());
373    }
374}