async-graphql/async-graphql-parser/src/value.rs

160 lines
5.0 KiB
Rust

use std::collections::BTreeMap;
use std::fmt;
/// Represents a GraphQL value
#[derive(Clone, Debug)]
#[allow(missing_docs)]
pub enum Value {
Null,
Variable(String),
Int(i64),
Float(f64),
String(String),
Boolean(bool),
Enum(String),
List(Vec<Value>),
Object(BTreeMap<String, Value>),
}
impl PartialEq for Value {
fn eq(&self, other: &Self) -> bool {
use Value::*;
match (self, other) {
(Variable(a), Variable(b)) => a.eq(b),
(Int(a), Int(b)) => a.eq(b),
(Float(a), Float(b)) => a.eq(b),
(String(a), String(b)) => a.eq(b),
(Boolean(a), Boolean(b)) => a.eq(b),
(Null, Null) => true,
(Enum(a), Enum(b)) => a.eq(b),
(List(a), List(b)) => {
if a.len() != b.len() {
return false;
}
for i in 0..a.len() {
if !a[i].eq(&b[i]) {
return false;
}
}
true
}
(Object(a), Object(b)) => {
if a.len() != b.len() {
return false;
}
for (key, a_value) in a.iter() {
if let Some(b_value) = b.get(key) {
if !a_value.eq(b_value) {
return false;
}
} else {
return false;
}
}
true
}
_ => false,
}
}
}
fn write_quoted(s: &str, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "\"")?;
for c in s.chars() {
match c {
'\r' => write!(f, "\r")?,
'\n' => writeln!(f)?,
'\t' => write!(f, "\t")?,
'"' => write!(f, "\"")?,
'\\' => write!(f, "\\")?,
'\u{0020}'..='\u{FFFF}' => write!(f, "{}", c)?,
_ => write!(f, "\\u{:04}", c as u32).unwrap(),
}
}
write!(f, "\"")
}
impl fmt::Display for Value {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Value::Variable(name) => write!(f, "${}", name),
Value::Int(num) => write!(f, "{}", *num),
Value::Float(val) => write!(f, "{}", *val),
Value::String(ref val) => write_quoted(val, f),
Value::Boolean(true) => write!(f, "true"),
Value::Boolean(false) => write!(f, "false"),
Value::Null => write!(f, "null"),
Value::Enum(ref name) => write!(f, "{}", name),
Value::List(ref items) => {
write!(f, "[")?;
if !items.is_empty() {
write!(f, "{}", items[0])?;
for item in &items[1..] {
write!(f, ", ")?;
write!(f, "{}", item)?;
}
}
write!(f, "]")
}
Value::Object(items) => {
write!(f, "{{")?;
let mut first = true;
for (name, value) in items {
if first {
first = false;
} else {
write!(f, ", ")?;
}
write!(f, "{}", name)?;
write!(f, ": ")?;
write!(f, "{}", value)?;
}
write!(f, "}}")
}
}
}
}
impl From<Value> for serde_json::Value {
fn from(value: Value) -> Self {
match value {
Value::Null => serde_json::Value::Null,
Value::Variable(name) => name.into(),
Value::Int(n) => n.into(),
Value::Float(n) => n.into(),
Value::String(s) => s.into(),
Value::Boolean(v) => v.into(),
Value::Enum(e) => e.into(),
Value::List(values) => values
.into_iter()
.map(Into::into)
.collect::<Vec<serde_json::Value>>()
.into(),
Value::Object(obj) => serde_json::Value::Object(
obj.into_iter()
.map(|(name, value)| (name, value.into()))
.collect(),
),
}
}
}
impl From<serde_json::Value> for Value {
fn from(value: serde_json::Value) -> Self {
match value {
serde_json::Value::Null => Value::Null,
serde_json::Value::Bool(n) => Value::Boolean(n),
serde_json::Value::Number(n) if n.is_f64() => Value::Float(n.as_f64().unwrap()),
serde_json::Value::Number(n) => Value::Int(n.as_i64().unwrap()),
serde_json::Value::String(s) => Value::String(s),
serde_json::Value::Array(ls) => Value::List(ls.into_iter().map(Into::into).collect()),
serde_json::Value::Object(obj) => Value::Object(
obj.into_iter()
.map(|(name, value)| (name, value.into()))
.collect(),
),
}
}
}