use crate::Error;
use juniper::{DefaultScalarValue, FromInputValue, InputValue};
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::convert::{TryFrom, TryInto};
use std::fmt::Result as FmtResult;
use std::fmt::{Display, Formatter};
use uuid::Uuid;
#[derive(Clone, Debug, Deserialize, Serialize)]
pub enum Value {
Array(Vec<Value>),
Bool(bool),
Float64(f64),
Int64(i64),
Map(HashMap<String, Value>),
Null,
String(String),
UInt64(u64),
Uuid(Uuid),
}
impl Display for Value {
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
write!(
f,
"{}",
match &self {
Value::Array(v) => {
let s = v
.iter()
.enumerate()
.fold("[".to_string(), |mut acc, (i, val)| {
if i > 0 {
acc.push_str(", ");
}
acc.push_str(&*format!("{}", val));
acc
});
s + "]"
}
Value::Bool(b) => b.to_string(),
Value::Float64(f) => f.to_string(),
Value::Int64(i) => i.to_string(),
Value::Map(m) => {
let s =
m.iter()
.enumerate()
.fold("[".to_string(), |mut acc, (i, (key, val))| {
if i > 0 {
acc.push_str(", ");
}
acc.push_str(&*format!("({}, {})", key, val));
acc
});
s + "]"
}
Value::Null => "{}".to_string(),
Value::String(s) => s.to_string(),
Value::UInt64(u) => u.to_string(),
Value::Uuid(uuid) => uuid.to_hyphenated().to_string(),
}
)
}
}
impl From<bool> for Value {
fn from(v: bool) -> Self {
Value::Bool(v)
}
}
impl From<f64> for Value {
fn from(v: f64) -> Self {
Value::Float64(v)
}
}
impl From<HashMap<String, Value>> for Value {
fn from(map: HashMap<String, Value>) -> Self {
Value::Map(map)
}
}
impl From<i64> for Value {
fn from(v: i64) -> Self {
Value::Int64(v)
}
}
impl From<String> for Value {
fn from(v: String) -> Self {
Value::String(v)
}
}
impl From<u64> for Value {
fn from(v: u64) -> Self {
Value::UInt64(v)
}
}
impl From<Uuid> for Value {
fn from(v: Uuid) -> Self {
Value::Uuid(v)
}
}
impl From<Vec<Value>> for Value {
fn from(v: Vec<Value>) -> Self {
Value::Array(v)
}
}
impl FromInputValue for Value {
fn from_input_value(v: &InputValue) -> Option<Self> {
match v {
InputValue::Scalar(scalar) => Some(match scalar {
DefaultScalarValue::Int(i) => Value::Int64(i64::from(*i)),
DefaultScalarValue::Float(f) => Value::Float64(*f),
DefaultScalarValue::String(s) => Value::String(s.to_string()),
DefaultScalarValue::Boolean(b) => Value::Bool(*b),
}),
_ => match serde_json::to_value(v) {
Err(_) => None,
Ok(serde_value) => match Value::try_from(serde_value) {
Ok(value) => Some(value),
_ => None,
},
},
}
}
}
impl PartialEq for Value {
fn eq(&self, other: &Value) -> bool {
match (self, other) {
(Value::Array(a), Value::Array(oa)) => a == oa,
(Value::Bool(b), Value::Bool(ob)) => b == ob,
(Value::Float64(f), Value::Float64(of)) => f == of,
(Value::Int64(i), Value::Int64(oi)) => i == oi,
(Value::Map(m), Value::Map(om)) => m == om,
(Value::Null, Value::Null) => true,
(Value::String(s), Value::String(os)) => s == os,
(Value::UInt64(i), Value::UInt64(oi)) => i == oi,
(_, _) => false,
}
}
}
impl TryFrom<serde_json::Value> for Value {
type Error = Error;
fn try_from(value: serde_json::Value) -> Result<Value, Error> {
match value {
serde_json::Value::Array(a) => Ok(Value::Array(
a.into_iter()
.map(|val| val.try_into())
.collect::<Result<Vec<_>, _>>()?,
)),
serde_json::Value::Bool(b) => Ok(Value::Bool(b)),
serde_json::Value::Null => Ok(Value::Null),
serde_json::Value::Number(n) => {
if let Some(i) = n.as_i64() {
Ok(Value::Int64(i))
} else if let Some(i) = n.as_u64() {
Ok(Value::UInt64(i))
} else if let Some(f) = n.as_f64() {
Ok(Value::Float64(f))
} else {
Err(Error::TypeConversionFailed {
src: "serde_json::Value::Number".to_string(),
dst: "Value".to_string(),
})
}
}
serde_json::Value::String(s) => Ok(Value::String(s)),
serde_json::Value::Object(m) => Ok(Value::Map(
m.into_iter()
.map(|(k, v)| {
let val = v.try_into()?;
Ok((k, val))
})
.collect::<Result<HashMap<String, Value>, Error>>()?,
)),
}
}
}
impl TryFrom<Value> for bool {
type Error = Error;
fn try_from(value: Value) -> Result<bool, Self::Error> {
if let Value::Bool(b) = value {
Ok(b)
} else {
Err(Error::TypeConversionFailed {
src: format!("{:#?}", value),
dst: "bool".to_string(),
})
}
}
}
impl TryFrom<Value> for f64 {
type Error = Error;
fn try_from(value: Value) -> Result<f64, Self::Error> {
if let Value::Int64(i) = value {
Ok(i as f64)
} else if let Value::UInt64(i) = value {
Ok(i as f64)
} else if let Value::Float64(f) = value {
Ok(f)
} else {
Err(Error::TypeConversionFailed {
src: format!("{:#?}", value),
dst: "f64".to_string(),
})
}
}
}
impl TryFrom<Value> for i32 {
type Error = Error;
fn try_from(value: Value) -> Result<i32, Self::Error> {
match value {
Value::Int64(i) => Ok(i32::try_from(i)?),
Value::UInt64(i) => Ok(i32::try_from(i)?),
_ => Err(Error::TypeConversionFailed {
src: format!("{:#?}", value),
dst: "i32".to_string(),
}),
}
}
}
impl TryFrom<Value> for String {
type Error = Error;
fn try_from(value: Value) -> Result<String, Self::Error> {
if let Value::String(s) = value {
Ok(s)
} else if let Value::Int64(i) = value {
Ok(i.to_string())
} else {
Err(Error::TypeConversionFailed {
src: format!("{:#?}", value),
dst: "String".to_string(),
})
}
}
}
impl TryFrom<Value> for serde_json::Value {
type Error = Error;
fn try_from(value: Value) -> Result<serde_json::Value, Error> {
match value {
Value::Array(a) => Ok(serde_json::Value::Array(
a.into_iter()
.map(|v| v.try_into())
.collect::<Result<Vec<_>, Error>>()?,
)),
Value::Bool(b) => Ok(serde_json::Value::Bool(b)),
Value::Float64(f) => Ok(serde_json::Value::Number(
serde_json::Number::from_f64(f).ok_or_else(|| Error::TypeConversionFailed {
src: "Value::Float64".to_string(),
dst: "serde_json::Number".to_string(),
})?,
)),
Value::Int64(i) => Ok(serde_json::Value::Number(i.into())),
Value::Map(hm) => Ok(serde_json::Value::Object(
hm.into_iter()
.map(|(k, v)| {
let val = v.try_into()?;
Ok((k, val))
})
.collect::<Result<serde_json::Map<String, serde_json::Value>, Error>>()?,
)),
Value::Null => Ok(serde_json::Value::Null),
Value::String(s) => Ok(serde_json::Value::String(s)),
Value::UInt64(i) => Ok(serde_json::Value::Number(i.into())),
Value::Uuid(uuid) => Ok(serde_json::Value::String(uuid.to_hyphenated().to_string())),
}
}
}
impl<T> TryFrom<Value> for Vec<T>
where
T: TryFrom<Value, Error = Error>,
{
type Error = Error;
fn try_from(value: Value) -> Result<Self, Self::Error> {
if let Value::Array(a) = value {
if let Some(Value::Null) = a.get(0) {
Ok(Vec::new())
} else {
a.into_iter()
.map(|v| v.try_into())
.collect::<Result<Vec<_>, Error>>()
}
} else {
Err(Error::TypeConversionFailed {
src: format!("{:#?}", value),
dst: "<T> where T: TryFrom<Value, Error = Error>".to_string(),
})
}
}
}
impl TryFrom<Value> for HashMap<String, Value> {
type Error = Error;
fn try_from(value: Value) -> Result<HashMap<String, Value>, Error> {
match value {
Value::Map(hm) => Ok(hm),
_ => Err(Error::TypeConversionFailed {
src: format!("{:#?}", value),
dst: "HashMap<String, Value>".to_string(),
}),
}
}
}
#[cfg(test)]
mod tests {
use super::Value;
#[test]
fn test_value_send() {
fn assert_send<T: Send>() {}
assert_send::<Value>();
}
#[test]
fn test_value_sync() {
fn assert_sync<T: Sync>() {}
assert_sync::<Value>();
}
}