handlebars/json/
value.rs

1use serde::Serialize;
2use serde_json::value::{to_value, Value as Json};
3
4pub(crate) static DEFAULT_VALUE: Json = Json::Null;
5
6/// A JSON wrapper designed for handlebars internal use case
7///
8/// * Constant: the JSON value hardcoded into template
9/// * Context:  the JSON value referenced in your provided data context
10/// * Derived:  the owned JSON value computed during rendering process
11///
12#[derive(Debug, Clone)]
13pub enum ScopedJson<'rc> {
14    Constant(&'rc Json),
15    Derived(Json),
16    // represents a json reference to context value, its full path
17    Context(&'rc Json, Vec<String>),
18    Missing,
19}
20
21impl<'rc> ScopedJson<'rc> {
22    /// get the JSON reference
23    pub fn as_json(&self) -> &Json {
24        match self {
25            ScopedJson::Constant(j) => j,
26            ScopedJson::Derived(ref j) => j,
27            ScopedJson::Context(j, _) => j,
28            _ => &DEFAULT_VALUE,
29        }
30    }
31
32    pub fn render(&self) -> String {
33        self.as_json().render()
34    }
35
36    pub fn is_missing(&self) -> bool {
37        matches!(self, ScopedJson::Missing)
38    }
39
40    pub fn into_derived(self) -> ScopedJson<'rc> {
41        let v = self.as_json();
42        ScopedJson::Derived(v.clone())
43    }
44
45    pub fn context_path(&self) -> Option<&Vec<String>> {
46        match self {
47            ScopedJson::Context(_, ref p) => Some(p),
48            _ => None,
49        }
50    }
51}
52
53impl<'reg: 'rc, 'rc> From<Json> for ScopedJson<'rc> {
54    fn from(v: Json) -> ScopedJson<'rc> {
55        ScopedJson::Derived(v)
56    }
57}
58
59/// Json wrapper that holds the Json value and reference path information
60///
61#[derive(Debug, Clone)]
62pub struct PathAndJson<'rc> {
63    relative_path: Option<String>,
64    value: ScopedJson<'rc>,
65}
66
67impl<'rc> PathAndJson<'rc> {
68    pub fn new(relative_path: Option<String>, value: ScopedJson<'rc>) -> PathAndJson<'rc> {
69        PathAndJson {
70            relative_path,
71            value,
72        }
73    }
74
75    /// Returns relative path when the value is referenced
76    /// If the value is from a literal, the path is `None`
77    pub fn relative_path(&self) -> Option<&String> {
78        self.relative_path.as_ref()
79    }
80
81    /// Returns full path to this value if any
82    pub fn context_path(&self) -> Option<&Vec<String>> {
83        self.value.context_path()
84    }
85
86    /// Returns the value
87    pub fn value(&self) -> &Json {
88        self.value.as_json()
89    }
90
91    /// Test if value is missing
92    pub fn is_value_missing(&self) -> bool {
93        self.value.is_missing()
94    }
95
96    pub fn render(&self) -> String {
97        self.value.render()
98    }
99}
100
101/// Render Json data with default format
102pub trait JsonRender {
103    fn render(&self) -> String;
104}
105
106pub trait JsonTruthy {
107    fn is_truthy(&self, include_zero: bool) -> bool;
108}
109
110impl JsonRender for Json {
111    fn render(&self) -> String {
112        match *self {
113            Json::String(ref s) => s.to_string(),
114            Json::Bool(i) => i.to_string(),
115            Json::Number(ref n) => n.to_string(),
116            Json::Null => "".to_owned(),
117            Json::Array(ref a) => {
118                let mut buf = String::new();
119                buf.push('[');
120                for (i, value) in a.iter().enumerate() {
121                    buf.push_str(value.render().as_ref());
122
123                    if i < a.len() - 1 {
124                        buf.push_str(", ");
125                    }
126                }
127                buf.push(']');
128                buf
129            }
130            Json::Object(_) => "[object]".to_owned(),
131        }
132    }
133}
134
135/// Convert any serializable data into Serde Json type
136pub fn to_json<T>(src: T) -> Json
137where
138    T: Serialize,
139{
140    to_value(src).unwrap_or_default()
141}
142
143pub fn as_string(src: &Json) -> Option<&str> {
144    src.as_str()
145}
146
147impl JsonTruthy for Json {
148    fn is_truthy(&self, include_zero: bool) -> bool {
149        match *self {
150            Json::Bool(ref i) => *i,
151            Json::Number(ref n) => {
152                if include_zero {
153                    n.as_f64().map(|f| !f.is_nan()).unwrap_or(false)
154                } else {
155                    // there is no inifity in json/serde_json
156                    n.as_f64().map(|f| f.is_normal()).unwrap_or(false)
157                }
158            }
159            Json::Null => false,
160            Json::String(ref i) => !i.is_empty(),
161            Json::Array(ref i) => !i.is_empty(),
162            Json::Object(ref i) => !i.is_empty(),
163        }
164    }
165}
166
167#[test]
168fn test_json_render() {
169    let raw = "<p>Hello world</p>\n<p thing=\"hello\"</p>";
170    let thing = Json::String(raw.to_string());
171
172    assert_eq!(raw, thing.render());
173}
174
175#[test]
176fn test_json_number_truthy() {
177    use std::f64;
178    assert!(json!(16i16).is_truthy(false));
179    assert!(json!(16i16).is_truthy(true));
180
181    assert!(json!(0i16).is_truthy(true));
182    assert!(!json!(0i16).is_truthy(false));
183
184    assert!(json!(1.0f64).is_truthy(false));
185    assert!(json!(1.0f64).is_truthy(true));
186
187    assert!(json!(Some(16i16)).is_truthy(false));
188    assert!(json!(Some(16i16)).is_truthy(true));
189
190    assert!(!json!(None as Option<i16>).is_truthy(false));
191    assert!(!json!(None as Option<i16>).is_truthy(true));
192
193    assert!(!json!(f64::NAN).is_truthy(false));
194    assert!(!json!(f64::NAN).is_truthy(true));
195
196    // there is no infinity in json/serde_json
197    // assert!(json!(f64::INFINITY).is_truthy(false));
198    // assert!(json!(f64::INFINITY).is_truthy(true));
199
200    // assert!(json!(f64::NEG_INFINITY).is_truthy(false));
201    // assert!(json!(f64::NEG_INFINITY).is_truthy(true));
202}