loose_liquid_core/model/value/
values.rs

1#![allow(clippy::eq_op)]
2
3use std::cmp::Ordering;
4use std::fmt;
5
6use crate::model::KStringCow;
7
8use super::DisplayCow;
9use super::State;
10use super::{ValueView, ValueViewCmp};
11use crate::model::array::{Array, ArrayView};
12use crate::model::object::{Object, ObjectView};
13use crate::model::scalar::{Scalar, ScalarCow};
14
15/// An enum to represent different value types
16#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
17#[serde(untagged)]
18pub enum Value {
19    /// A scalar value.
20    Scalar(Scalar),
21    /// A sequence of `Value`s.
22    Array(Array),
23    /// A sequence of key/`Value` pairs.
24    Object(Object),
25    /// Query symbol.
26    State(State),
27    /// Nothing.
28    Nil,
29}
30
31impl Value {
32    /// Create as a `Scalar`.
33    pub fn scalar<T: Into<Scalar>>(value: T) -> Self {
34        Value::Scalar(ScalarCow::new(value))
35    }
36
37    /// Create as an `Array`.
38    pub fn array<I: IntoIterator<Item = Value>>(iter: I) -> Value {
39        let v: Array = iter.into_iter().collect();
40        Value::Array(v)
41    }
42
43    /// Performs the conversion.
44    pub fn as_view(&self) -> &dyn ValueView {
45        match &self {
46            Value::Scalar(ref x) => x,
47            Value::Object(ref x) => x,
48            Value::Array(ref x) => x,
49            Value::State(ref x) => x,
50            Value::Nil => self,
51        }
52    }
53
54    /// Extracts the scalar value if it is a scalar.
55    pub fn into_scalar(self) -> Option<Scalar> {
56        match self {
57            Value::Scalar(s) => Some(s),
58            _ => None,
59        }
60    }
61
62    /// Extracts the array value if it is an array.
63    pub fn into_array(self) -> Option<Array> {
64        match self {
65            Value::Array(s) => Some(s),
66            _ => None,
67        }
68    }
69
70    /// Extracts the array value as mutable if it is a object.
71    pub fn as_array_mut(&mut self) -> Option<&mut Array> {
72        match *self {
73            Value::Array(ref mut s) => Some(s),
74            _ => None,
75        }
76    }
77
78    /// Extracts the object value if it is a object.
79    pub fn into_object(self) -> Option<Object> {
80        match self {
81            Value::Object(s) => Some(s),
82            _ => None,
83        }
84    }
85
86    /// Extracts the object value as mutable if it is a object.
87    pub fn as_object_mut(&mut self) -> Option<&mut Object> {
88        match *self {
89            Value::Object(ref mut s) => Some(s),
90            _ => None,
91        }
92    }
93
94    /// Extracts the state if it is one
95    pub fn into_state(self) -> Option<State> {
96        match self {
97            Value::State(s) => Some(s),
98            _ => None,
99        }
100    }
101}
102
103impl ValueView for Value {
104    fn as_debug(&self) -> &dyn fmt::Debug {
105        self
106    }
107
108    fn render(&self) -> DisplayCow<'_> {
109        match *self {
110            Value::Scalar(ref x) => x.render(),
111            Value::Array(ref x) => x.render(),
112            Value::Object(ref x) => x.render(),
113            Value::State(ref x) => x.render(),
114            Value::Nil => DisplayCow::Borrowed(&""),
115        }
116    }
117    fn source(&self) -> DisplayCow<'_> {
118        match *self {
119            Value::Scalar(ref x) => x.source(),
120            Value::Array(ref x) => x.source(),
121            Value::Object(ref x) => x.source(),
122            Value::State(ref x) => x.source(),
123            Value::Nil => DisplayCow::Owned(Box::new(super::StrDisplay {
124                s: self.type_name(),
125            })),
126        }
127    }
128    fn type_name(&self) -> &'static str {
129        match *self {
130            Value::Scalar(ref x) => x.type_name(),
131            Value::Array(ref x) => x.type_name(),
132            Value::Object(ref x) => x.type_name(),
133            Value::State(ref x) => x.type_name(),
134            Value::Nil => "nil",
135        }
136    }
137    fn query_state(&self, state: State) -> bool {
138        match *self {
139            Value::Scalar(ref x) => x.query_state(state),
140            Value::Array(ref x) => x.query_state(state),
141            Value::Object(ref x) => x.query_state(state),
142            Value::State(ref x) => x.query_state(state),
143            Value::Nil => match state {
144                State::Truthy => false,
145                State::DefaultValue => true,
146                State::Empty => true,
147                State::Blank => true,
148            },
149        }
150    }
151
152    fn to_kstr(&self) -> KStringCow<'_> {
153        match *self {
154            Value::Scalar(ref x) => x.to_kstr(),
155            Value::Array(ref x) => x.to_kstr(),
156            Value::Object(ref x) => x.to_kstr(),
157            Value::State(ref x) => x.to_kstr(),
158            Value::Nil => KStringCow::from_static(""),
159        }
160    }
161    fn to_value(&self) -> Value {
162        match self {
163            Value::Scalar(ref x) => {
164                let x = x.clone();
165                let x = x.into_owned();
166                Value::Scalar(x)
167            }
168            Value::Array(ref x) => Value::Array(x.clone()),
169            Value::Object(ref x) => Value::Object(x.clone()),
170            Value::State(ref x) => Value::State(*x),
171            Value::Nil => Value::Nil,
172        }
173    }
174
175    fn as_scalar(&self) -> Option<ScalarCow<'_>> {
176        match self {
177            Value::Scalar(s) => Some(s.as_ref()),
178            _ => None,
179        }
180    }
181    fn as_array(&self) -> Option<&dyn ArrayView> {
182        match self {
183            Value::Array(ref s) => Some(s),
184            _ => None,
185        }
186    }
187    fn as_object(&self) -> Option<&dyn ObjectView> {
188        match self {
189            Value::Object(ref s) => Some(s),
190            _ => None,
191        }
192    }
193    fn as_state(&self) -> Option<State> {
194        match self {
195            Value::State(s) => Some(*s),
196            _ => None,
197        }
198    }
199    fn is_nil(&self) -> bool {
200        matches!(self, Value::Nil)
201    }
202}
203
204impl From<Scalar> for Value {
205    fn from(other: Scalar) -> Self {
206        Value::Scalar(other)
207    }
208}
209
210impl From<Array> for Value {
211    fn from(other: Array) -> Self {
212        Value::Array(other)
213    }
214}
215
216impl From<Object> for Value {
217    fn from(other: Object) -> Self {
218        Value::Object(other)
219    }
220}
221
222impl From<State> for Value {
223    fn from(other: State) -> Self {
224        Value::State(other)
225    }
226}
227
228impl Default for Value {
229    fn default() -> Self {
230        Self::Nil
231    }
232}
233
234impl PartialEq<Value> for Value {
235    fn eq(&self, other: &Self) -> bool {
236        super::value_eq(self.as_view(), other.as_view())
237    }
238}
239
240impl<'v> PartialEq<ValueViewCmp<'v>> for Value {
241    fn eq(&self, other: &ValueViewCmp<'v>) -> bool {
242        ValueViewCmp::new(self.as_view()) == *other
243    }
244}
245
246impl PartialEq<i64> for Value {
247    fn eq(&self, other: &i64) -> bool {
248        super::value_eq(self.as_view(), other)
249    }
250}
251
252impl PartialEq<f64> for Value {
253    fn eq(&self, other: &f64) -> bool {
254        super::value_eq(self.as_view(), other)
255    }
256}
257
258impl PartialEq<bool> for Value {
259    fn eq(&self, other: &bool) -> bool {
260        super::value_eq(self.as_view(), other)
261    }
262}
263
264impl PartialEq<crate::model::scalar::DateTime> for Value {
265    fn eq(&self, other: &crate::model::scalar::DateTime) -> bool {
266        super::value_eq(self.as_view(), other)
267    }
268}
269
270impl PartialEq<crate::model::scalar::Date> for Value {
271    fn eq(&self, other: &crate::model::scalar::Date) -> bool {
272        super::value_eq(self.as_view(), other)
273    }
274}
275
276impl PartialEq<str> for Value {
277    fn eq(&self, other: &str) -> bool {
278        let other = KStringCow::from_ref(other);
279        super::value_eq(self.as_view(), &other)
280    }
281}
282
283impl<'s> PartialEq<&'s str> for Value {
284    fn eq(&self, other: &&str) -> bool {
285        super::value_eq(self.as_view(), other)
286    }
287}
288
289impl PartialEq<String> for Value {
290    fn eq(&self, other: &String) -> bool {
291        super::value_eq(self.as_view(), other)
292    }
293}
294
295impl PartialEq<crate::model::KString> for Value {
296    fn eq(&self, other: &crate::model::KString) -> bool {
297        super::value_eq(self.as_view(), &other.as_ref())
298    }
299}
300
301impl<'s> PartialEq<crate::model::KStringRef<'s>> for Value {
302    fn eq(&self, other: &crate::model::KStringRef<'s>) -> bool {
303        super::value_eq(self.as_view(), other)
304    }
305}
306
307impl<'s> PartialEq<crate::model::KStringCow<'s>> for Value {
308    fn eq(&self, other: &crate::model::KStringCow<'s>) -> bool {
309        super::value_eq(self.as_view(), other)
310    }
311}
312
313impl Eq for Value {}
314
315impl PartialOrd<Value> for Value {
316    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
317        super::value_cmp(self.as_view(), other)
318    }
319}
320
321#[cfg(test)]
322mod test {
323    use super::*;
324
325    #[test]
326    fn test_to_string_scalar() {
327        let val = Value::scalar(42f64);
328        assert_eq!(&val.render().to_string(), "42");
329        assert_eq!(&val.to_kstr(), "42");
330    }
331
332    #[test]
333    fn test_to_string_array() {
334        let val = Value::Array(vec![
335            Value::scalar(3f64),
336            Value::scalar("test"),
337            Value::scalar(5.3),
338        ]);
339        assert_eq!(&val.render().to_string(), "3test5.3");
340        assert_eq!(&val.to_kstr(), "3test5.3");
341    }
342
343    // TODO make a test for object, remember values are in arbitrary orders in HashMaps
344
345    #[test]
346    fn test_to_string_nil() {
347        assert_eq!(&Value::Nil.render().to_string(), "");
348        assert_eq!(&Value::Nil.to_kstr(), "");
349    }
350
351    #[test]
352    fn scalar_equality() {
353        assert_eq!(Value::scalar("alpha"), Value::scalar("alpha"));
354        assert_eq!(Value::scalar(""), Value::scalar(""));
355        assert!(Value::scalar("alpha") != Value::scalar("beta"));
356        assert!(Value::scalar("beta") != Value::scalar("alpha"));
357    }
358
359    #[test]
360    fn scalars_have_ruby_truthiness() {
361        // all strings in ruby are true
362        assert_eq!(Value::scalar(true), Value::scalar("All strings are truthy"));
363        assert_eq!(Value::scalar(true), Value::scalar(""));
364        assert!(Value::scalar("").query_state(State::Truthy));
365
366        assert_eq!(Value::scalar(true), Value::scalar(true));
367        assert!(Value::scalar(true) != Value::scalar(false));
368    }
369
370    #[test]
371    fn array_equality() {
372        let a = Value::Array(vec![Value::scalar("one"), Value::scalar("two")]);
373        let b = Value::Array(vec![Value::scalar("alpha"), Value::scalar("beta")]);
374
375        assert_eq!(a, a);
376        assert!(a != b);
377        assert!(b != a);
378    }
379
380    #[test]
381    fn arrays_have_ruby_truthiness() {
382        assert_eq!(Value::scalar(true), Value::Array(Vec::new()));
383        assert!(Value::Array(Vec::new()).query_state(State::Truthy));
384    }
385
386    #[test]
387    fn object_equality() {
388        let a: Object = [
389            ("alpha".into(), Value::scalar("1")),
390            ("beta".into(), Value::scalar(2f64)),
391        ]
392        .iter()
393        .cloned()
394        .collect();
395        let a = Value::Object(a);
396
397        let b: Object = [
398            ("alpha".into(), Value::scalar("1")),
399            ("beta".into(), Value::scalar(2f64)),
400            ("gamma".into(), Value::Array(vec![])),
401        ]
402        .iter()
403        .cloned()
404        .collect();
405        let b = Value::Object(b);
406
407        assert_eq!(a, a);
408        assert!(a != b);
409        assert!(b != a);
410    }
411
412    #[test]
413    fn objects_have_ruby_truthiness() {
414        assert_eq!(Value::scalar(true), Value::Object(Object::new()));
415        assert!(Value::Object(Object::new()).query_state(State::Truthy));
416    }
417
418    #[test]
419    fn nil_equality() {
420        assert_eq!(Value::Nil, Value::Nil);
421    }
422
423    #[test]
424    fn nils_have_ruby_truthiness() {
425        assert_eq!(Value::scalar(false), Value::Nil);
426        assert!(!Value::Nil.query_state(State::Truthy));
427
428        assert_eq!(Value::scalar(false), Value::Nil);
429        assert!(Value::scalar(true) != Value::Nil);
430        assert!(Value::scalar("") != Value::Nil);
431    }
432
433    #[test]
434    fn empty_equality() {
435        let blank = Value::State(State::Blank);
436        let empty = Value::State(State::Empty);
437        // Truth table from https://stackoverflow.com/questions/885414/a-concise-explanation-of-nil-v-empty-v-blank-in-ruby-on-rails
438        assert_eq!(empty, empty);
439        assert_eq!(empty, blank);
440        assert_eq!(empty, value!(""));
441        assert_ne!(empty, value!(" "));
442        assert_eq!(empty, value!([]));
443        assert_ne!(empty, value!([nil]));
444        assert_eq!(empty, value!({}));
445        assert_ne!(empty, value!({ "a": nil }));
446    }
447
448    #[test]
449    fn blank_equality() {
450        let blank = Value::State(State::Blank);
451        let empty = Value::State(State::Empty);
452        // Truth table from https://stackoverflow.com/questions/885414/a-concise-explanation-of-nil-v-empty-v-blank-in-ruby-on-rails
453        assert_eq!(blank, blank);
454        assert_eq!(blank, empty);
455        assert_eq!(blank, value!(nil));
456        assert_eq!(blank, value!(false));
457        assert_ne!(blank, value!(true));
458        assert_ne!(blank, value!(0));
459        assert_ne!(blank, value!(1));
460        assert_eq!(blank, value!(""));
461        assert_eq!(blank, value!(" "));
462        assert_eq!(blank, value!([]));
463        assert_ne!(blank, value!([nil]));
464        assert_eq!(blank, value!({}));
465        assert_ne!(blank, value!({ "a": nil }));
466    }
467}