warpgrapher/engine/
value.rs

1use crate::Error;
2use juniper::{DefaultScalarValue, FromInputValue, InputValue};
3use serde::{Deserialize, Serialize};
4use std::collections::HashMap;
5use std::convert::{TryFrom, TryInto};
6use std::fmt::Result as FmtResult;
7use std::fmt::{Display, Formatter};
8use uuid::Uuid;
9
10/// Intermediate data structure for serialized values, allowing for translation between the values
11/// returned by the back-end database (serde_json for cypher-based databases, and a
12/// library-specific seralized format for Cosmos and Gremlin DBs), and the serde_json format used
13/// to return data to the client.
14///
15/// # Examples
16///
17/// ```rust
18/// # use warpgrapher::engine::value::Value;
19///
20/// let v = Value::Bool(true);
21/// ```
22#[derive(Clone, Debug, Deserialize, Serialize)]
23pub enum Value {
24    Array(Vec<Value>),
25    Bool(bool),
26    Float64(f64),
27    Int64(i64),
28    Map(HashMap<String, Value>),
29    Null,
30    String(String),
31    UInt64(u64),
32    Uuid(Uuid),
33}
34
35impl Display for Value {
36    fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
37        write!(
38            f,
39            "{}",
40            match &self {
41                Value::Array(v) => {
42                    let s = v
43                        .iter()
44                        .enumerate()
45                        .fold("[".to_string(), |mut acc, (i, val)| {
46                            if i > 0 {
47                                acc.push_str(", ");
48                            }
49
50                            acc.push_str(&*format!("{}", val));
51                            acc
52                        });
53                    s + "]"
54                }
55                Value::Bool(b) => b.to_string(),
56                Value::Float64(f) => f.to_string(),
57                Value::Int64(i) => i.to_string(),
58                Value::Map(m) => {
59                    let s =
60                        m.iter()
61                            .enumerate()
62                            .fold("[".to_string(), |mut acc, (i, (key, val))| {
63                                if i > 0 {
64                                    acc.push_str(", ");
65                                }
66
67                                acc.push_str(&*format!("({}, {})", key, val));
68                                acc
69                            });
70
71                    s + "]"
72                }
73                Value::Null => "{}".to_string(),
74                Value::String(s) => s.to_string(),
75                Value::UInt64(u) => u.to_string(),
76                Value::Uuid(uuid) => uuid.to_hyphenated().to_string(),
77            }
78        )
79    }
80}
81
82impl From<bool> for Value {
83    fn from(v: bool) -> Self {
84        Value::Bool(v)
85    }
86}
87
88impl From<f64> for Value {
89    fn from(v: f64) -> Self {
90        Value::Float64(v)
91    }
92}
93
94impl From<HashMap<String, Value>> for Value {
95    fn from(map: HashMap<String, Value>) -> Self {
96        Value::Map(map)
97    }
98}
99
100impl From<i64> for Value {
101    fn from(v: i64) -> Self {
102        Value::Int64(v)
103    }
104}
105
106impl From<String> for Value {
107    fn from(v: String) -> Self {
108        Value::String(v)
109    }
110}
111
112impl From<u64> for Value {
113    fn from(v: u64) -> Self {
114        Value::UInt64(v)
115    }
116}
117
118impl From<Uuid> for Value {
119    fn from(v: Uuid) -> Self {
120        Value::Uuid(v)
121    }
122}
123
124impl From<Vec<Value>> for Value {
125    fn from(v: Vec<Value>) -> Self {
126        Value::Array(v)
127    }
128}
129
130impl FromInputValue for Value {
131    fn from_input_value(v: &InputValue) -> Option<Self> {
132        match v {
133            InputValue::Scalar(scalar) => Some(match scalar {
134                DefaultScalarValue::Int(i) => Value::Int64(i64::from(*i)),
135                DefaultScalarValue::Float(f) => Value::Float64(*f),
136                DefaultScalarValue::String(s) => Value::String(s.to_string()),
137                DefaultScalarValue::Boolean(b) => Value::Bool(*b),
138            }),
139            _ => match serde_json::to_value(v) {
140                Err(_) => None,
141                Ok(serde_value) => match Value::try_from(serde_value) {
142                    Ok(value) => Some(value),
143                    _ => None,
144                },
145            },
146        }
147    }
148}
149
150impl PartialEq for Value {
151    fn eq(&self, other: &Value) -> bool {
152        match (self, other) {
153            (Value::Array(a), Value::Array(oa)) => a == oa,
154            (Value::Bool(b), Value::Bool(ob)) => b == ob,
155            (Value::Float64(f), Value::Float64(of)) => f == of,
156            (Value::Int64(i), Value::Int64(oi)) => i == oi,
157            (Value::Map(m), Value::Map(om)) => m == om,
158            (Value::Null, Value::Null) => true,
159            (Value::String(s), Value::String(os)) => s == os,
160            (Value::UInt64(i), Value::UInt64(oi)) => i == oi,
161            (_, _) => false,
162        }
163    }
164}
165
166impl TryFrom<serde_json::Value> for Value {
167    type Error = Error;
168
169    fn try_from(value: serde_json::Value) -> Result<Value, Error> {
170        match value {
171            serde_json::Value::Array(a) => Ok(Value::Array(
172                a.into_iter()
173                    .map(|val| val.try_into())
174                    .collect::<Result<Vec<_>, _>>()?,
175            )),
176            serde_json::Value::Bool(b) => Ok(Value::Bool(b)),
177            serde_json::Value::Null => Ok(Value::Null),
178            serde_json::Value::Number(n) => {
179                if let Some(i) = n.as_i64() {
180                    Ok(Value::Int64(i))
181                } else if let Some(i) = n.as_u64() {
182                    Ok(Value::UInt64(i))
183                } else if let Some(f) = n.as_f64() {
184                    Ok(Value::Float64(f))
185                } else {
186                    Err(Error::TypeConversionFailed {
187                        src: "serde_json::Value::Number".to_string(),
188                        dst: "Value".to_string(),
189                    })
190                }
191            }
192            serde_json::Value::String(s) => Ok(Value::String(s)),
193            serde_json::Value::Object(m) => Ok(Value::Map(
194                m.into_iter()
195                    .map(|(k, v)| {
196                        let val = v.try_into()?;
197                        Ok((k, val))
198                    })
199                    .collect::<Result<HashMap<String, Value>, Error>>()?,
200            )),
201        }
202    }
203}
204
205impl TryFrom<Value> for bool {
206    type Error = Error;
207
208    fn try_from(value: Value) -> Result<bool, Self::Error> {
209        if let Value::Bool(b) = value {
210            Ok(b)
211        } else {
212            Err(Error::TypeConversionFailed {
213                src: format!("{:#?}", value),
214                dst: "bool".to_string(),
215            })
216        }
217    }
218}
219
220impl TryFrom<Value> for f64 {
221    type Error = Error;
222
223    fn try_from(value: Value) -> Result<f64, Self::Error> {
224        if let Value::Int64(i) = value {
225            Ok(i as f64)
226        } else if let Value::UInt64(i) = value {
227            Ok(i as f64)
228        } else if let Value::Float64(f) = value {
229            Ok(f)
230        } else {
231            Err(Error::TypeConversionFailed {
232                src: format!("{:#?}", value),
233                dst: "f64".to_string(),
234            })
235        }
236    }
237}
238
239impl TryFrom<Value> for i32 {
240    type Error = Error;
241
242    fn try_from(value: Value) -> Result<i32, Self::Error> {
243        match value {
244            Value::Int64(i) => Ok(i32::try_from(i)?),
245            Value::UInt64(i) => Ok(i32::try_from(i)?),
246            _ => Err(Error::TypeConversionFailed {
247                src: format!("{:#?}", value),
248                dst: "i32".to_string(),
249            }),
250        }
251    }
252}
253
254impl TryFrom<Value> for String {
255    type Error = Error;
256
257    fn try_from(value: Value) -> Result<String, Self::Error> {
258        if let Value::String(s) = value {
259            Ok(s)
260        } else if let Value::Int64(i) = value {
261            Ok(i.to_string())
262        } else {
263            Err(Error::TypeConversionFailed {
264                src: format!("{:#?}", value),
265                dst: "String".to_string(),
266            })
267        }
268    }
269}
270
271impl TryFrom<Value> for serde_json::Value {
272    type Error = Error;
273
274    fn try_from(value: Value) -> Result<serde_json::Value, Error> {
275        match value {
276            Value::Array(a) => Ok(serde_json::Value::Array(
277                a.into_iter()
278                    .map(|v| v.try_into())
279                    .collect::<Result<Vec<_>, Error>>()?,
280            )),
281            Value::Bool(b) => Ok(serde_json::Value::Bool(b)),
282            Value::Float64(f) => Ok(serde_json::Value::Number(
283                serde_json::Number::from_f64(f).ok_or_else(|| Error::TypeConversionFailed {
284                    src: "Value::Float64".to_string(),
285                    dst: "serde_json::Number".to_string(),
286                })?,
287            )),
288            Value::Int64(i) => Ok(serde_json::Value::Number(i.into())),
289            Value::Map(hm) => Ok(serde_json::Value::Object(
290                hm.into_iter()
291                    .map(|(k, v)| {
292                        let val = v.try_into()?;
293                        Ok((k, val))
294                    })
295                    .collect::<Result<serde_json::Map<String, serde_json::Value>, Error>>()?,
296            )),
297            Value::Null => Ok(serde_json::Value::Null),
298            Value::String(s) => Ok(serde_json::Value::String(s)),
299            Value::UInt64(i) => Ok(serde_json::Value::Number(i.into())),
300            Value::Uuid(uuid) => Ok(serde_json::Value::String(uuid.to_hyphenated().to_string())),
301        }
302    }
303}
304
305impl<T> TryFrom<Value> for Vec<T>
306where
307    T: TryFrom<Value, Error = Error>,
308{
309    type Error = Error;
310
311    fn try_from(value: Value) -> Result<Self, Self::Error> {
312        if let Value::Array(a) = value {
313            if let Some(Value::Null) = a.get(0) {
314                // If the array null values, return an empty vector, indicating null to Juniper.
315                Ok(Vec::new())
316            } else {
317                // If the array is other than null, try to do the conversation to a Vector.
318                a.into_iter()
319                    .map(|v| v.try_into())
320                    .collect::<Result<Vec<_>, Error>>()
321            }
322        } else {
323            Err(Error::TypeConversionFailed {
324                src: format!("{:#?}", value),
325                dst: "<T> where T: TryFrom<Value, Error = Error>".to_string(),
326            })
327        }
328    }
329}
330
331impl TryFrom<Value> for HashMap<String, Value> {
332    type Error = Error;
333
334    fn try_from(value: Value) -> Result<HashMap<String, Value>, Error> {
335        match value {
336            Value::Map(hm) => Ok(hm),
337            _ => Err(Error::TypeConversionFailed {
338                src: format!("{:#?}", value),
339                dst: "HashMap<String, Value>".to_string(),
340            }),
341        }
342    }
343}
344
345#[cfg(test)]
346mod tests {
347    use super::Value;
348
349    /// Passes if the Value implements the Send trait
350    #[test]
351    fn test_value_send() {
352        fn assert_send<T: Send>() {}
353        assert_send::<Value>();
354    }
355
356    /// Passes if Value implements the Sync trait
357    #[test]
358    fn test_value_sync() {
359        fn assert_sync<T: Sync>() {}
360        assert_sync::<Value>();
361    }
362}