1 //! Type representing a Liquid object, payload of the `Value::Object` variant
2
3 pub mod map;
4 mod ser;
5
6 use std::collections::BTreeMap;
7 use std::collections::HashMap;
8 use std::fmt;
9
10 use kstring::KStringCow;
11
12 use crate::model::value::DisplayCow;
13 use crate::model::State;
14 use crate::model::{Value, ValueView};
15
16 pub use map::Object;
17 pub use ser::to_object;
18
19 /// Accessor for objects.
20 pub trait ObjectView: ValueView {
21 /// Cast to ValueView
as_value(&self) -> &dyn ValueView22 fn as_value(&self) -> &dyn ValueView;
23
24 /// Returns the number of elements.
size(&self) -> i6425 fn size(&self) -> i64;
26
27 /// Keys available for lookup.
keys<'k>(&'k self) -> Box<dyn Iterator<Item = KStringCow<'k>> + 'k>28 fn keys<'k>(&'k self) -> Box<dyn Iterator<Item = KStringCow<'k>> + 'k>;
29 /// Keys available for lookup.
values<'k>(&'k self) -> Box<dyn Iterator<Item = &'k dyn ValueView> + 'k>30 fn values<'k>(&'k self) -> Box<dyn Iterator<Item = &'k dyn ValueView> + 'k>;
31 /// Returns an iterator .
iter<'k>(&'k self) -> Box<dyn Iterator<Item = (KStringCow<'k>, &'k dyn ValueView)> + 'k>32 fn iter<'k>(&'k self) -> Box<dyn Iterator<Item = (KStringCow<'k>, &'k dyn ValueView)> + 'k>;
33
34 /// Access a contained `BoxedValue`.
contains_key(&self, index: &str) -> bool35 fn contains_key(&self, index: &str) -> bool;
36 /// Access a contained `Value`.
get<'s>(&'s self, index: &str) -> Option<&'s dyn ValueView>37 fn get<'s>(&'s self, index: &str) -> Option<&'s dyn ValueView>;
38 }
39
40 impl ValueView for Object {
as_debug(&self) -> &dyn fmt::Debug41 fn as_debug(&self) -> &dyn fmt::Debug {
42 self
43 }
44
render(&self) -> DisplayCow<'_>45 fn render(&self) -> DisplayCow<'_> {
46 DisplayCow::Owned(Box::new(ObjectRender { s: self }))
47 }
source(&self) -> DisplayCow<'_>48 fn source(&self) -> DisplayCow<'_> {
49 DisplayCow::Owned(Box::new(ObjectSource { s: self }))
50 }
type_name(&self) -> &'static str51 fn type_name(&self) -> &'static str {
52 "object"
53 }
query_state(&self, state: State) -> bool54 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
to_kstr(&self) -> KStringCow<'_>61 fn to_kstr(&self) -> KStringCow<'_> {
62 let s = ObjectRender { s: self }.to_string();
63 KStringCow::from_string(s)
64 }
to_value(&self) -> Value65 fn to_value(&self) -> Value {
66 Value::Object(self.clone())
67 }
68
as_object(&self) -> Option<&dyn ObjectView>69 fn as_object(&self) -> Option<&dyn ObjectView> {
70 Some(self)
71 }
72 }
73
74 impl ObjectView for Object {
as_value(&self) -> &dyn ValueView75 fn as_value(&self) -> &dyn ValueView {
76 self
77 }
78
size(&self) -> i6479 fn size(&self) -> i64 {
80 self.len() as i64
81 }
82
keys<'k>(&'k self) -> Box<dyn Iterator<Item = KStringCow<'k>> + 'k>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
values<'k>(&'k self) -> Box<dyn Iterator<Item = &'k dyn ValueView> + 'k>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
iter<'k>(&'k self) -> Box<dyn Iterator<Item = (KStringCow<'k>, &'k dyn ValueView)> + 'k>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
contains_key(&self, index: &str) -> bool98 fn contains_key(&self, index: &str) -> bool {
99 Object::contains_key(self, index)
100 }
101
get<'s>(&'s self, index: &str) -> Option<&'s dyn ValueView>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
107 impl<'o, O: ObjectView + ?Sized> ObjectView for &'o O {
as_value(&self) -> &dyn ValueView108 fn as_value(&self) -> &dyn ValueView {
109 <O as ObjectView>::as_value(self)
110 }
111
size(&self) -> i64112 fn size(&self) -> i64 {
113 <O as ObjectView>::size(self)
114 }
115
keys<'k>(&'k self) -> Box<dyn Iterator<Item = KStringCow<'k>> + 'k>116 fn keys<'k>(&'k self) -> Box<dyn Iterator<Item = KStringCow<'k>> + 'k> {
117 <O as ObjectView>::keys(self)
118 }
119
values<'k>(&'k self) -> Box<dyn Iterator<Item = &'k dyn ValueView> + 'k>120 fn values<'k>(&'k self) -> Box<dyn Iterator<Item = &'k dyn ValueView> + 'k> {
121 <O as ObjectView>::values(self)
122 }
123
iter<'k>(&'k self) -> Box<dyn Iterator<Item = (KStringCow<'k>, &'k dyn ValueView)> + 'k>124 fn iter<'k>(&'k self) -> Box<dyn Iterator<Item = (KStringCow<'k>, &'k dyn ValueView)> + 'k> {
125 <O as ObjectView>::iter(self)
126 }
127
contains_key(&self, index: &str) -> bool128 fn contains_key(&self, index: &str) -> bool {
129 <O as ObjectView>::contains_key(self, index)
130 }
131
get<'s>(&'s self, index: &str) -> Option<&'s dyn ValueView>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
138 pub trait ObjectIndex:
139 fmt::Debug + fmt::Display + Ord + std::hash::Hash + Eq + std::borrow::Borrow<str>
140 {
141 /// Borrow the index
as_index(&self) -> &str142 fn as_index(&self) -> &str;
143 }
144
145 impl ObjectIndex for String {
as_index(&self) -> &str146 fn as_index(&self) -> &str {
147 self.as_str()
148 }
149 }
150
151 impl ObjectIndex for kstring::KString {
as_index(&self) -> &str152 fn as_index(&self) -> &str {
153 self.as_str()
154 }
155 }
156
157 impl<'s> ObjectIndex for kstring::KStringRef<'s> {
as_index(&self) -> &str158 fn as_index(&self) -> &str {
159 self.as_str()
160 }
161 }
162
163 impl<'s> ObjectIndex for kstring::KStringCow<'s> {
as_index(&self) -> &str164 fn as_index(&self) -> &str {
165 self.as_str()
166 }
167 }
168
169 impl<K: ObjectIndex, V: ValueView, S: ::std::hash::BuildHasher> ValueView for HashMap<K, V, S> {
as_debug(&self) -> &dyn fmt::Debug170 fn as_debug(&self) -> &dyn fmt::Debug {
171 self
172 }
173
render(&self) -> DisplayCow<'_>174 fn render(&self) -> DisplayCow<'_> {
175 DisplayCow::Owned(Box::new(ObjectRender { s: self }))
176 }
source(&self) -> DisplayCow<'_>177 fn source(&self) -> DisplayCow<'_> {
178 DisplayCow::Owned(Box::new(ObjectSource { s: self }))
179 }
type_name(&self) -> &'static str180 fn type_name(&self) -> &'static str {
181 "object"
182 }
query_state(&self, state: State) -> bool183 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
to_kstr(&self) -> KStringCow<'_>190 fn to_kstr(&self) -> KStringCow<'_> {
191 let s = ObjectRender { s: self }.to_string();
192 KStringCow::from_string(s)
193 }
to_value(&self) -> Value194 fn to_value(&self) -> Value {
195 Value::Object(
196 self.iter()
197 .map(|(k, v)| (kstring::KString::from_ref(k.as_index()), v.to_value()))
198 .collect(),
199 )
200 }
201
as_object(&self) -> Option<&dyn ObjectView>202 fn as_object(&self) -> Option<&dyn ObjectView> {
203 Some(self)
204 }
205 }
206
207 impl<K: ObjectIndex, V: ValueView, S: ::std::hash::BuildHasher> ObjectView for HashMap<K, V, S> {
as_value(&self) -> &dyn ValueView208 fn as_value(&self) -> &dyn ValueView {
209 self
210 }
211
size(&self) -> i64212 fn size(&self) -> i64 {
213 self.len() as i64
214 }
215
keys<'k>(&'k self) -> Box<dyn Iterator<Item = KStringCow<'k>> + 'k>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
values<'k>(&'k self) -> Box<dyn Iterator<Item = &'k dyn ValueView> + 'k>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
iter<'k>(&'k self) -> Box<dyn Iterator<Item = (KStringCow<'k>, &'k dyn ValueView)> + 'k>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
contains_key(&self, index: &str) -> bool231 fn contains_key(&self, index: &str) -> bool {
232 HashMap::contains_key(self, index)
233 }
234
get<'s>(&'s self, index: &str) -> Option<&'s dyn ValueView>235 fn get<'s>(&'s self, index: &str) -> Option<&'s dyn ValueView> {
236 HashMap::get(self, index).map(as_view)
237 }
238 }
239
240 impl<K: ObjectIndex, V: ValueView> ValueView for BTreeMap<K, V> {
as_debug(&self) -> &dyn fmt::Debug241 fn as_debug(&self) -> &dyn fmt::Debug {
242 self
243 }
244
render(&self) -> DisplayCow<'_>245 fn render(&self) -> DisplayCow<'_> {
246 DisplayCow::Owned(Box::new(ObjectRender { s: self }))
247 }
source(&self) -> DisplayCow<'_>248 fn source(&self) -> DisplayCow<'_> {
249 DisplayCow::Owned(Box::new(ObjectSource { s: self }))
250 }
type_name(&self) -> &'static str251 fn type_name(&self) -> &'static str {
252 "object"
253 }
query_state(&self, state: State) -> bool254 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
to_kstr(&self) -> KStringCow<'_>261 fn to_kstr(&self) -> KStringCow<'_> {
262 let s = ObjectRender { s: self }.to_string();
263 KStringCow::from_string(s)
264 }
to_value(&self) -> Value265 fn to_value(&self) -> Value {
266 Value::Object(
267 self.iter()
268 .map(|(k, v)| (kstring::KString::from_ref(k.as_index()), v.to_value()))
269 .collect(),
270 )
271 }
272
as_object(&self) -> Option<&dyn ObjectView>273 fn as_object(&self) -> Option<&dyn ObjectView> {
274 Some(self)
275 }
276 }
277
278 impl<K: ObjectIndex, V: ValueView> ObjectView for BTreeMap<K, V> {
as_value(&self) -> &dyn ValueView279 fn as_value(&self) -> &dyn ValueView {
280 self
281 }
282
size(&self) -> i64283 fn size(&self) -> i64 {
284 self.len() as i64
285 }
286
keys<'k>(&'k self) -> Box<dyn Iterator<Item = KStringCow<'k>> + 'k>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
values<'k>(&'k self) -> Box<dyn Iterator<Item = &'k dyn ValueView> + 'k>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
iter<'k>(&'k self) -> Box<dyn Iterator<Item = (KStringCow<'k>, &'k dyn ValueView)> + 'k>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
contains_key(&self, index: &str) -> bool302 fn contains_key(&self, index: &str) -> bool {
303 BTreeMap::contains_key(self, index)
304 }
305
get<'s>(&'s self, index: &str) -> Option<&'s dyn ValueView>306 fn get<'s>(&'s self, index: &str) -> Option<&'s dyn ValueView> {
307 BTreeMap::get(self, index).map(as_view)
308 }
309 }
310
as_view<T: ValueView>(value: &T) -> &dyn ValueView311 fn as_view<T: ValueView>(value: &T) -> &dyn ValueView {
312 value
313 }
314
315 #[derive(Debug)]
316 /// Helper for `ObjectView::source`
317 pub struct ObjectSource<'s, O: ObjectView> {
318 s: &'s O,
319 }
320
321 impl<'s, O: ObjectView> ObjectSource<'s, O> {
322 #[doc(hidden)]
new(other: &'s O) -> Self323 pub fn new(other: &'s O) -> Self {
324 Self { s: other }
325 }
326 }
327
328 impl<'s, O: ObjectView> fmt::Display for ObjectSource<'s, O> {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result329 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`
341 pub struct ObjectRender<'s, O: ObjectView> {
342 s: &'s O,
343 }
344
345 impl<'s, O: ObjectView> ObjectRender<'s, O> {
346 #[doc(hidden)]
new(other: &'s O) -> Self347 pub fn new(other: &'s O) -> Self {
348 Self { s: other }
349 }
350 }
351
352 impl<'s, O: ObjectView> fmt::Display for ObjectRender<'s, O> {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result353 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)]
362 mod test {
363 use super::*;
364
365 #[test]
test_object()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 }
375