use std::fmt::Display;
use ordermap::OrderMap;
use serde::{
Serialize,
ser::{SerializeMap, SerializeSeq},
};
use crate::{errors::JsonError, number::Number};
#[derive(Debug, Clone, PartialEq, PartialOrd)]
pub enum Value {
Null,
Bool(bool),
Number(Number),
String(String),
Array(Vec<Value>),
Map(OrderMap<String, Value>),
}
impl From<bool> for Value {
fn from(value: bool) -> Self {
Self::Bool(value)
}
}
impl From<u8> for Value {
fn from(value: u8) -> Self {
Self::Number(value.into())
}
}
impl From<u16> for Value {
fn from(value: u16) -> Self {
Self::Number(value.into())
}
}
impl From<u32> for Value {
fn from(value: u32) -> Self {
Self::Number(value.into())
}
}
impl From<u64> for Value {
fn from(value: u64) -> Self {
Self::Number(value.into())
}
}
impl From<i8> for Value {
fn from(value: i8) -> Self {
Self::Number(value.into())
}
}
impl From<i16> for Value {
fn from(value: i16) -> Self {
Self::Number(value.into())
}
}
impl From<i32> for Value {
fn from(value: i32) -> Self {
Self::Number(value.into())
}
}
impl From<i64> for Value {
fn from(value: i64) -> Self {
Self::Number(value.into())
}
}
impl From<f32> for Value {
fn from(value: f32) -> Self {
Self::Number(value.into())
}
}
impl From<f64> for Value {
fn from(value: f64) -> Self {
Self::Number(value.into())
}
}
impl From<&str> for Value {
fn from(value: &str) -> Self {
Self::String(value.to_string())
}
}
impl From<String> for Value {
fn from(value: String) -> Self {
Self::String(value)
}
}
impl<T> From<Vec<T>> for Value
where
T: Into<Value>,
{
fn from(value: Vec<T>) -> Self {
Self::Array(value.into_iter().map(Into::into).collect())
}
}
impl TryFrom<serde_json::Value> for Value {
type Error = JsonError;
fn try_from(value: serde_json::Value) -> Result<Self, Self::Error> {
Ok(match value {
serde_json::Value::Null => Self::Null,
serde_json::Value::Bool(value) => Self::Bool(value),
serde_json::Value::Number(value) => Self::Number(value.try_into()?),
serde_json::Value::String(value) => Self::String(value),
serde_json::Value::Array(values) => Self::Array(
values
.into_iter()
.map(Value::try_from)
.collect::<Result<Vec<Value>, Self::Error>>()?,
),
serde_json::Value::Object(value) => Self::Map(value.into_iter().try_fold(
OrderMap::new(),
|mut acc, (k, v)| {
acc.insert(k, v.try_into()?);
Ok(acc)
},
)?),
})
}
}
impl Serialize for Value {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
match self {
Self::Null => serializer.serialize_none(),
Self::Bool(b) => serializer.serialize_bool(*b),
Self::Number(n) => n.serialize(serializer),
Self::String(s) => serializer.serialize_str(s),
Self::Array(values) => {
let mut seq = serializer.serialize_seq(Some(values.len()))?;
for v in values {
seq.serialize_element(v)?;
}
seq.end()
}
Self::Map(value) => {
let mut map = serializer.serialize_map(Some(value.len()))?;
for (k, v) in value {
map.serialize_entry(k, v)?;
}
map.end()
}
}
}
}
impl Display for Value {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Value::Null => write!(f, "null"),
Value::Bool(b) => write!(f, "{}", b),
Value::Number(n) => write!(f, "{}", n),
Value::String(s) => write!(f, "\"{}\"", s),
Value::Array(values) => {
write!(
f,
"[{}]",
values
.iter()
.map(|v| format!("{}", v))
.collect::<Vec<String>>()
.join(", ")
)
}
Value::Map(map) => write!(
f,
"{{ {} }}",
map.iter()
.map(|(k, v)| format!("{}: {}", k, v))
.collect::<Vec<String>>()
.join(",")
),
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct Variables(OrderMap<String, Value>);
impl TryFrom<serde_json::Value> for Variables {
type Error = JsonError;
fn try_from(value: serde_json::Value) -> Result<Self, Self::Error> {
match value {
serde_json::Value::Object(map) => Ok(Self(map.into_iter().try_fold(
OrderMap::new(),
|mut acc, (k, v)| {
acc.insert(k, v.try_into()?);
Ok(acc)
},
)?)),
_ => Err(JsonError::ExpectingAnObject),
}
}
}
impl From<OrderMap<String, Value>> for Variables {
fn from(value: OrderMap<String, Value>) -> Self {
Self(value)
}
}
impl Serialize for Variables {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
let mut map = serializer.serialize_map(Some(self.0.len()))?;
for (k, v) in &self.0 {
map.serialize_entry(k, v)?;
}
map.end()
}
}
impl Display for Variables {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"{{ {} }}",
self.0
.iter()
.map(|(k, v)| { format!("{}: {}", k, v) })
.collect::<Vec<String>>()
.join(", ")
)
}
}
impl Variables {
pub fn new() -> Self {
Self(OrderMap::new())
}
pub fn set<S: AsRef<str>, V: Into<Value>>(&mut self, name: S, value: V) {
self.0.insert(name.as_ref().to_string(), value.into());
}
pub fn get<S: AsRef<str>>(&self, name: S) -> Option<&Value> {
self.0.get(name.as_ref())
}
pub fn merge(&mut self, other: &Self) -> &Self {
for (k, v) in &other.0 {
self.0.insert(k.to_string(), v.clone());
}
self
}
}
#[macro_export]
macro_rules! vars {
{$($k:expr => $v:expr),* $(,)?} => {
{
let mut temp_vars = Variables::new();
$(
temp_vars.set($k, $v);
)*
temp_vars
}
};
}
#[macro_export]
macro_rules! map {
{$($k:expr => $v:expr),* $(,)?} => {
{
let mut temp_map = ordermap::OrderMap::new();
$(
temp_map.insert($k.to_string(), $v.into());
)*
Value::Map(temp_map)
}
};
}
#[cfg(test)]
mod test {
use crate::variables::{Value, Variables};
#[test]
fn can_load_variables_from_json() {
let test_cases = vec![
(r#"{"string": "value"}"#, vars! {"string" => "value"}),
(r#"{"number": 123}"#, vars! {"number" => 123u64}),
(
r#"{"null": null, "bool": true, "number": -1, "string": "something"}"#,
vars! {
"null" => Value::Null,
"bool" => true,
"number" => -1i64,
"string" => "something",
},
),
(
r#"{"strings": ["hello", "world"]}"#,
vars! {
"strings" => vec!["hello", "world"],
},
),
(
r#"{"object": {"hello": 123, "object": true, "number": false}}"#,
vars! {
"object" => map! {
"hello" => 123,
"object" => true,
"number" => false,
},
},
),
];
for test_case in test_cases {
let (json, expected_variables) = test_case;
let json_value = serde_json::from_str::<serde_json::Value>(json)
.expect("to be able to deserialize the JSON string");
let actual_variables = Variables::try_from(json_value)
.expect("to be able to convert a JSON value to variables");
assert_eq!(actual_variables, expected_variables,);
}
}
}