use crate::pos::{Pos, Positioned};
use serde::de::{Deserializer, Error as _, Unexpected};
use serde::ser::{Error as _, Serializer};
use serde::{Deserialize, Serialize};
use std::borrow::Borrow;
use std::collections::{BTreeMap, HashMap};
use std::convert::{TryFrom, TryInto};
use std::fmt::{self, Display, Formatter, Write};
use std::fs::File;
use std::ops::Deref;
pub use executable::*;
pub use serde_json::Number;
pub use service::*;
mod executable;
mod service;
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
pub enum OperationType {
Query,
Mutation,
Subscription,
}
impl Display for OperationType {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str(match self {
Self::Query => "query",
Self::Mutation => "mutation",
Self::Subscription => "subscription",
})
}
}
#[derive(Debug, PartialEq, Eq, Clone)]
pub struct Type {
pub base: BaseType,
pub nullable: bool,
}
impl Type {
#[must_use]
pub fn new(ty: &str) -> Option<Self> {
let (nullable, ty) = if let Some(rest) = ty.strip_suffix('!') {
(false, rest)
} else {
(true, ty)
};
Some(Self {
base: if let Some(ty) = ty.strip_prefix('[') {
BaseType::List(Box::new(Self::new(ty.strip_suffix(']')?)?))
} else {
BaseType::Named(Name::new(ty.to_owned()).ok()?)
},
nullable,
})
}
}
impl Display for Type {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
self.base.fmt(f)?;
if !self.nullable {
f.write_char('!')?;
}
Ok(())
}
}
#[derive(Debug, PartialEq, Eq, Clone)]
pub enum BaseType {
Named(Name),
List(Box<Type>),
}
impl Display for BaseType {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match self {
Self::Named(name) => f.write_str(name),
Self::List(ty) => write!(f, "[{}]", ty),
}
}
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(untagged)]
pub enum ConstValue {
Null,
Number(Number),
String(String),
Boolean(bool),
#[serde(skip_deserializing)]
Enum(Name),
List(Vec<ConstValue>),
Object(BTreeMap<Name, ConstValue>),
#[serde(serialize_with = "fail_serialize_upload", skip_deserializing)]
Upload(UploadValue),
}
impl ConstValue {
#[must_use]
pub fn into_value(self) -> Value {
match self {
Self::Null => Value::Null,
Self::Number(num) => Value::Number(num),
Self::String(s) => Value::String(s),
Self::Boolean(b) => Value::Boolean(b),
Self::Enum(v) => Value::Enum(v),
Self::List(items) => {
Value::List(items.into_iter().map(ConstValue::into_value).collect())
}
Self::Object(map) => Value::Object(
map.into_iter()
.map(|(key, value)| (key, value.into_value()))
.collect(),
),
Self::Upload(upload) => Value::Upload(upload),
}
}
pub fn into_json(self) -> serde_json::Result<serde_json::Value> {
self.try_into()
}
pub fn from_json(json: serde_json::Value) -> serde_json::Result<Self> {
json.try_into()
}
}
impl Default for ConstValue {
fn default() -> Self {
Self::Null
}
}
impl Display for ConstValue {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match self {
Self::Number(num) => write!(f, "{}", *num),
Self::String(val) => write_quoted(val, f),
Self::Boolean(true) => f.write_str("true"),
Self::Boolean(false) => f.write_str("false"),
Self::Null | Self::Upload(_) => f.write_str("null"),
Self::Enum(name) => f.write_str(name),
Self::List(items) => write_list(items, f),
Self::Object(map) => write_object(map, f),
}
}
}
impl TryFrom<serde_json::Value> for ConstValue {
type Error = serde_json::Error;
fn try_from(value: serde_json::Value) -> Result<Self, Self::Error> {
Self::deserialize(value)
}
}
impl TryFrom<ConstValue> for serde_json::Value {
type Error = serde_json::Error;
fn try_from(value: ConstValue) -> Result<Self, Self::Error> {
serde_json::to_value(value)
}
}
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
#[serde(untagged)]
pub enum Value {
#[serde(serialize_with = "fail_serialize_variable", skip_deserializing)]
Variable(Name),
Null,
Number(Number),
String(String),
Boolean(bool),
#[serde(skip_deserializing)]
Enum(Name),
List(Vec<Value>),
Object(BTreeMap<Name, Value>),
#[serde(serialize_with = "fail_serialize_upload", skip_deserializing)]
Upload(UploadValue),
}
impl Value {
pub fn into_const_with<E>(
self,
mut f: impl FnMut(Name) -> Result<ConstValue, E>,
) -> Result<ConstValue, E> {
self.into_const_with_mut(&mut f)
}
fn into_const_with_mut<E>(
self,
f: &mut impl FnMut(Name) -> Result<ConstValue, E>,
) -> Result<ConstValue, E> {
Ok(match self {
Self::Variable(name) => f(name)?,
Self::Null => ConstValue::Null,
Self::Number(num) => ConstValue::Number(num),
Self::String(s) => ConstValue::String(s),
Self::Boolean(b) => ConstValue::Boolean(b),
Self::Enum(v) => ConstValue::Enum(v),
Self::List(items) => ConstValue::List(
items
.into_iter()
.map(|value| value.into_const_with_mut(f))
.collect::<Result<_, _>>()?,
),
Self::Object(map) => ConstValue::Object(
map.into_iter()
.map(|(key, value)| Ok((key, value.into_const_with_mut(f)?)))
.collect::<Result<_, _>>()?,
),
Self::Upload(upload) => ConstValue::Upload(upload),
})
}
#[must_use]
pub fn into_const(self) -> Option<ConstValue> {
self.into_const_with(|_| Err(())).ok()
}
pub fn into_json(self) -> serde_json::Result<serde_json::Value> {
self.try_into()
}
pub fn from_json(json: serde_json::Value) -> serde_json::Result<Self> {
json.try_into()
}
}
impl Default for Value {
fn default() -> Self {
Self::Null
}
}
impl Display for Value {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match self {
Self::Variable(name) => write!(f, "${}", name),
Self::Number(num) => write!(f, "{}", *num),
Self::String(val) => write_quoted(val, f),
Self::Boolean(true) => f.write_str("true"),
Self::Boolean(false) => f.write_str("false"),
Self::Null | Self::Upload(_) => f.write_str("null"),
Self::Enum(name) => f.write_str(name),
Self::List(items) => write_list(items, f),
Self::Object(map) => write_object(map, f),
}
}
}
impl From<ConstValue> for Value {
fn from(value: ConstValue) -> Self {
value.into_value()
}
}
impl TryFrom<serde_json::Value> for Value {
type Error = serde_json::Error;
fn try_from(value: serde_json::Value) -> Result<Self, Self::Error> {
Self::deserialize(value)
}
}
impl TryFrom<Value> for serde_json::Value {
type Error = serde_json::Error;
fn try_from(value: Value) -> Result<Self, Self::Error> {
serde_json::to_value(value)
}
}
fn fail_serialize_variable<S: Serializer>(_: &str, _: S) -> Result<S::Ok, S::Error> {
Err(S::Error::custom("cannot serialize variable"))
}
fn fail_serialize_upload<S: Serializer>(_: &UploadValue, _: S) -> Result<S::Ok, S::Error> {
Err(S::Error::custom("cannot serialize uploaded file"))
}
fn write_quoted(s: &str, f: &mut Formatter<'_>) -> fmt::Result {
f.write_char('"')?;
for c in s.chars() {
match c {
'\r' => f.write_str("\\r"),
'\n' => f.write_str("\\n"),
'\t' => f.write_str("\\t"),
'"' => f.write_str("\\\""),
'\\' => f.write_str("\\\\"),
c if c.is_control() => write!(f, "\\u{:04}", c as u32),
c => f.write_char(c),
}?
}
f.write_char('"')
}
fn write_list<T: Display>(list: impl IntoIterator<Item = T>, f: &mut Formatter<'_>) -> fmt::Result {
f.write_char('[')?;
for item in list {
item.fmt(f)?;
f.write_char(',')?;
}
f.write_char(']')
}
fn write_object<K: Display, V: Display>(
object: impl IntoIterator<Item = (K, V)>,
f: &mut Formatter<'_>,
) -> fmt::Result {
f.write_char('{')?;
for (name, value) in object {
write!(f, "{}: {},", name, value)?;
}
f.write_char('}')
}
pub struct UploadValue {
pub filename: String,
pub content_type: Option<String>,
pub content: File,
}
impl UploadValue {
pub fn try_clone(&self) -> std::io::Result<Self> {
Ok(Self {
filename: self.filename.clone(),
content_type: self.content_type.clone(),
content: self.content.try_clone()?,
})
}
}
impl Clone for UploadValue {
fn clone(&self) -> Self {
self.try_clone().unwrap()
}
}
impl fmt::Debug for UploadValue {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "Upload({})", self.filename)
}
}
impl PartialEq for UploadValue {
fn eq(&self, other: &Self) -> bool {
self.filename == other.filename
}
}
impl Eq for UploadValue {}
#[derive(Debug, Clone)]
pub struct ConstDirective {
pub name: Positioned<Name>,
pub arguments: Vec<(Positioned<Name>, Positioned<ConstValue>)>,
}
impl ConstDirective {
#[must_use]
pub fn into_directive(self) -> Directive {
Directive {
name: self.name,
arguments: self
.arguments
.into_iter()
.map(|(name, value)| (name, value.map(ConstValue::into_value)))
.collect(),
}
}
#[must_use]
pub fn get_argument(&self, name: &str) -> Option<&Positioned<ConstValue>> {
self.arguments
.iter()
.find(|item| item.0.node == name)
.map(|item| &item.1)
}
}
#[derive(Debug, Clone)]
pub struct Directive {
pub name: Positioned<Name>,
pub arguments: Vec<(Positioned<Name>, Positioned<Value>)>,
}
impl Directive {
#[must_use]
pub fn into_const(self) -> Option<ConstDirective> {
Some(ConstDirective {
name: self.name,
arguments: self
.arguments
.into_iter()
.map(|(name, value)| {
Some((name, Positioned::new(value.node.into_const()?, value.pos)))
})
.collect::<Option<_>>()?,
})
}
#[must_use]
pub fn get_argument(&self, name: &str) -> Option<&Positioned<Value>> {
self.arguments
.iter()
.find(|item| item.0.node == name)
.map(|item| &item.1)
}
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize)]
#[serde(transparent)]
pub struct Name(String);
impl Name {
#[must_use]
pub fn is_valid(name: &str) -> bool {
let mut bytes = name.bytes();
bytes
.next()
.map_or(false, |c| c.is_ascii_alphabetic() || c == b'_')
&& bytes.all(|c| c.is_ascii_alphanumeric() || c == b'_')
}
#[must_use]
pub fn new_unchecked(name: String) -> Self {
debug_assert!(Self::is_valid(&name));
Self(name)
}
pub fn new(name: String) -> Result<Self, String> {
if Self::is_valid(&name) {
Ok(Self(name))
} else {
Err(name)
}
}
#[must_use]
pub fn as_str(&self) -> &str {
&self.0
}
#[must_use]
pub fn into_string(self) -> String {
self.0
}
}
impl AsRef<str> for Name {
fn as_ref(&self) -> &str {
&self.0
}
}
impl Borrow<str> for Name {
fn borrow(&self) -> &str {
&self.0
}
}
impl From<Name> for String {
fn from(name: Name) -> Self {
name.0
}
}
impl Deref for Name {
type Target = str;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl Display for Name {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
Display::fmt(&self.0, f)
}
}
impl PartialEq<String> for Name {
fn eq(&self, other: &String) -> bool {
self.0 == *other
}
}
impl PartialEq<str> for Name {
fn eq(&self, other: &str) -> bool {
self.0 == other
}
}
impl PartialEq<Name> for String {
fn eq(&self, other: &Name) -> bool {
other == self
}
}
impl PartialEq<Name> for str {
fn eq(&self, other: &Name) -> bool {
other == self
}
}
impl<'a> PartialEq<&'a str> for Name {
fn eq(&self, other: &&'a str) -> bool {
self == *other
}
}
impl<'a> PartialEq<Name> for &'a str {
fn eq(&self, other: &Name) -> bool {
other == self
}
}
impl<'de> Deserialize<'de> for Name {
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
Self::new(String::deserialize(deserializer)?)
.map_err(|s| D::Error::invalid_value(Unexpected::Str(&s), &"a GraphQL name"))
}
}
#[cfg(test)]
#[test]
fn test_valid_names() {
assert!(Name::is_valid("valid_name"));
assert!(Name::is_valid("numbers123_456_789abc"));
assert!(Name::is_valid("MiXeD_CaSe"));
assert!(Name::is_valid("_"));
assert!(!Name::is_valid("invalid name"));
assert!(!Name::is_valid("123and_text"));
}