Skip to main content

online_dsl_forge/
value.rs

1use std::collections::BTreeMap;
2use std::convert::TryFrom;
3use std::fmt;
4
5use serde::{Deserialize, Serialize};
6
7#[derive(Debug, Clone, Deserialize, PartialEq, Serialize)]
8#[serde(tag = "type", content = "value", rename_all = "snake_case")]
9pub enum Value {
10  Null,
11  Bool(bool),
12  Int(i64),
13  Float(f64),
14  String(String),
15  Array(Vec<Value>),
16  Object(BTreeMap<String, Value>),
17}
18
19#[derive(Debug, Clone, Eq, PartialEq)]
20pub struct ValueConversionError {
21  message: String,
22}
23
24impl ValueConversionError {
25  fn new(message: impl Into<String>) -> Self {
26    Self {
27      message: message.into(),
28    }
29  }
30}
31
32impl fmt::Display for ValueConversionError {
33  fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
34    formatter.write_str(&self.message)
35  }
36}
37
38impl std::error::Error for ValueConversionError {}
39
40impl Value {
41  pub fn type_name(&self) -> &'static str {
42    match self {
43      Self::Null => "null",
44      Self::Bool(_) => "bool",
45      Self::Int(_) => "int",
46      Self::Float(_) => "float",
47      Self::String(_) => "string",
48      Self::Array(_) => "array",
49      Self::Object(_) => "object",
50    }
51  }
52
53  pub fn as_bool(&self) -> Option<bool> {
54    match self {
55      Self::Bool(value) => Some(*value),
56      _ => None,
57    }
58  }
59
60  pub fn is_number(&self) -> bool {
61    matches!(self, Self::Int(_) | Self::Float(_))
62  }
63}
64
65impl TryFrom<serde_json::Value> for Value {
66  type Error = ValueConversionError;
67
68  fn try_from(value: serde_json::Value) -> Result<Self, Self::Error> {
69    match value {
70      serde_json::Value::Null => Ok(Self::Null),
71      serde_json::Value::Bool(value) => Ok(Self::Bool(value)),
72      serde_json::Value::Number(number) => {
73        if let Some(value) = number.as_i64() {
74          Ok(Self::Int(value))
75        } else if let Some(value) = number.as_f64() {
76          Ok(Self::Float(value))
77        } else {
78          Err(ValueConversionError::new("unsupported JSON number"))
79        }
80      }
81      serde_json::Value::String(value) => Ok(Self::String(value)),
82      serde_json::Value::Array(values) => values
83        .into_iter()
84        .map(Value::try_from)
85        .collect::<Result<Vec<_>, _>>()
86        .map(Self::Array),
87      serde_json::Value::Object(values) => values
88        .into_iter()
89        .map(|(key, value)| Value::try_from(value).map(|value| (key, value)))
90        .collect::<Result<BTreeMap<_, _>, _>>()
91        .map(Self::Object),
92    }
93  }
94}
95
96impl From<Value> for serde_json::Value {
97  fn from(value: Value) -> Self {
98    match value {
99      Value::Null => Self::Null,
100      Value::Bool(value) => Self::Bool(value),
101      Value::Int(value) => Self::Number(value.into()),
102      Value::Float(value) => serde_json::Number::from_f64(value)
103        .map(Self::Number)
104        .unwrap_or(Self::Null),
105      Value::String(value) => Self::String(value),
106      Value::Array(values) => Self::Array(values.into_iter().map(Self::from).collect()),
107      Value::Object(values) => Self::Object(
108        values
109          .into_iter()
110          .map(|(key, value)| (key, Self::from(value)))
111          .collect(),
112      ),
113    }
114  }
115}