182 lines
5.0 KiB
Rust
182 lines
5.0 KiB
Rust
//! GraphQL types.
|
|
//!
|
|
//! The two root types are
|
|
//! [`ExecutableDocument`](struct.ExecutableDocument.html) and
|
|
//! [`ServiceDocument`](struct.ServiceDocument.html), representing an executable
|
|
//! GraphQL query and a GraphQL service respectively.
|
|
//!
|
|
//! This follows the [June 2018 edition of the GraphQL spec](https://spec.graphql.org/October2021/).
|
|
|
|
mod executable;
|
|
mod service;
|
|
|
|
use std::{
|
|
collections::{hash_map, HashMap},
|
|
fmt::{self, Display, Formatter, Write},
|
|
};
|
|
|
|
use async_graphql_value::{ConstValue, Name, Value};
|
|
pub use executable::*;
|
|
pub use service::*;
|
|
|
|
use crate::pos::Positioned;
|
|
|
|
/// The type of an operation; `query`, `mutation` or `subscription`.
|
|
///
|
|
/// [Reference](https://spec.graphql.org/October2021/#OperationType).
|
|
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
|
|
pub enum OperationType {
|
|
/// A query.
|
|
Query,
|
|
/// A mutation.
|
|
Mutation,
|
|
/// A subscription.
|
|
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",
|
|
})
|
|
}
|
|
}
|
|
|
|
/// A GraphQL type, for example `String` or `[String!]!`.
|
|
///
|
|
/// [Reference](https://spec.graphql.org/October2021/#Type).
|
|
#[derive(Debug, PartialEq, Eq, Clone)]
|
|
pub struct Type {
|
|
/// The base type.
|
|
pub base: BaseType,
|
|
/// Whether the type is nullable.
|
|
pub nullable: bool,
|
|
}
|
|
|
|
impl Type {
|
|
/// Create a type from the type string.
|
|
#[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))
|
|
},
|
|
nullable,
|
|
})
|
|
}
|
|
}
|
|
|
|
impl Display for Type {
|
|
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
|
self.base.fmt(f)?;
|
|
if !self.nullable {
|
|
f.write_char('!')?;
|
|
}
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
/// A GraphQL base type, for example `String` or `[String!]`. This does not
|
|
/// include whether the type is nullable; for that see [Type](struct.Type.html).
|
|
#[derive(Debug, PartialEq, Eq, Clone)]
|
|
pub enum BaseType {
|
|
/// A named type, such as `String`.
|
|
Named(Name),
|
|
/// A list type, such as `[String]`.
|
|
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),
|
|
}
|
|
}
|
|
}
|
|
|
|
/// A const GraphQL directive, such as `@deprecated(reason: "Use the other
|
|
/// field)`. This differs from [`Directive`](struct.Directive.html) in that it
|
|
/// uses [`ConstValue`](enum.ConstValue.html) instead of
|
|
/// [`Value`](enum.Value.html).
|
|
///
|
|
/// [Reference](https://spec.graphql.org/October2021/#Directive).
|
|
#[derive(Debug, Clone)]
|
|
pub struct ConstDirective {
|
|
/// The name of the directive.
|
|
pub name: Positioned<Name>,
|
|
/// The arguments to the directive.
|
|
pub arguments: Vec<(Positioned<Name>, Positioned<ConstValue>)>,
|
|
}
|
|
|
|
impl ConstDirective {
|
|
/// Convert this `ConstDirective` into a `Directive`.
|
|
#[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(),
|
|
}
|
|
}
|
|
|
|
/// Get the argument with the given name.
|
|
#[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)
|
|
}
|
|
}
|
|
|
|
/// A GraphQL directive, such as `@deprecated(reason: "Use the other field")`.
|
|
///
|
|
/// [Reference](https://spec.graphql.org/October2021/#Directive).
|
|
#[derive(Debug, Clone)]
|
|
pub struct Directive {
|
|
/// The name of the directive.
|
|
pub name: Positioned<Name>,
|
|
/// The arguments to the directive.
|
|
pub arguments: Vec<(Positioned<Name>, Positioned<Value>)>,
|
|
}
|
|
|
|
impl Directive {
|
|
/// Attempt to convert this `Directive` into a `ConstDirective`.
|
|
#[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<_>>()?,
|
|
})
|
|
}
|
|
|
|
/// Get the argument with the given name.
|
|
#[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)
|
|
}
|
|
}
|