async_graphql_parser/types/
mod.rs

1//! GraphQL types.
2//!
3//! The two root types are
4//! [`ExecutableDocument`](struct.ExecutableDocument.html) and
5//! [`ServiceDocument`](struct.ServiceDocument.html), representing an executable
6//! GraphQL query and a GraphQL service respectively.
7//!
8//! This follows the [June 2018 edition of the GraphQL spec](https://spec.graphql.org/October2021/).
9
10mod executable;
11mod service;
12
13use std::{
14    collections::{hash_map, HashMap},
15    fmt::{self, Display, Formatter, Write},
16};
17
18use async_graphql_value::{ConstValue, Name, Value};
19pub use executable::*;
20use serde::{Deserialize, Serialize};
21pub use service::*;
22
23use crate::pos::Positioned;
24
25/// The type of an operation; `query`, `mutation` or `subscription`.
26///
27/// [Reference](https://spec.graphql.org/October2021/#OperationType).
28#[derive(Debug, PartialEq, Eq, Copy, Clone, Serialize, Deserialize)]
29pub enum OperationType {
30    /// A query.
31    Query,
32    /// A mutation.
33    Mutation,
34    /// A subscription.
35    Subscription,
36}
37
38impl Display for OperationType {
39    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
40        f.write_str(match self {
41            Self::Query => "query",
42            Self::Mutation => "mutation",
43            Self::Subscription => "subscription",
44        })
45    }
46}
47
48/// A GraphQL type, for example `String` or `[String!]!`.
49///
50/// [Reference](https://spec.graphql.org/October2021/#Type).
51#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]
52pub struct Type {
53    /// The base type.
54    pub base: BaseType,
55    /// Whether the type is nullable.
56    pub nullable: bool,
57}
58
59impl Type {
60    /// Create a type from the type string.
61    #[must_use]
62    pub fn new(ty: &str) -> Option<Self> {
63        let (nullable, ty) = if let Some(rest) = ty.strip_suffix('!') {
64            (false, rest)
65        } else {
66            (true, ty)
67        };
68
69        Some(Self {
70            base: if let Some(ty) = ty.strip_prefix('[') {
71                BaseType::List(Box::new(Self::new(ty.strip_suffix(']')?)?))
72            } else {
73                BaseType::Named(Name::new(ty))
74            },
75            nullable,
76        })
77    }
78}
79
80impl Display for Type {
81    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
82        self.base.fmt(f)?;
83        if !self.nullable {
84            f.write_char('!')?;
85        }
86        Ok(())
87    }
88}
89
90/// A GraphQL base type, for example `String` or `[String!]`. This does not
91/// include whether the type is nullable; for that see [Type](struct.Type.html).
92#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]
93pub enum BaseType {
94    /// A named type, such as `String`.
95    Named(Name),
96    /// A list type, such as `[String]`.
97    List(Box<Type>),
98}
99
100impl Display for BaseType {
101    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
102        match self {
103            Self::Named(name) => f.write_str(name),
104            Self::List(ty) => write!(f, "[{}]", ty),
105        }
106    }
107}
108
109/// A const GraphQL directive, such as `@deprecated(reason: "Use the other
110/// field)`. This differs from [`Directive`](struct.Directive.html) in that it
111/// uses [`ConstValue`](enum.ConstValue.html) instead of
112/// [`Value`](enum.Value.html).
113///
114/// [Reference](https://spec.graphql.org/October2021/#Directive).
115#[derive(Debug, Clone)]
116pub struct ConstDirective {
117    /// The name of the directive.
118    pub name: Positioned<Name>,
119    /// The arguments to the directive.
120    pub arguments: Vec<(Positioned<Name>, Positioned<ConstValue>)>,
121}
122
123impl ConstDirective {
124    /// Convert this `ConstDirective` into a `Directive`.
125    #[must_use]
126    pub fn into_directive(self) -> Directive {
127        Directive {
128            name: self.name,
129            arguments: self
130                .arguments
131                .into_iter()
132                .map(|(name, value)| (name, value.map(ConstValue::into_value)))
133                .collect(),
134        }
135    }
136
137    /// Get the argument with the given name.
138    #[must_use]
139    pub fn get_argument(&self, name: &str) -> Option<&Positioned<ConstValue>> {
140        self.arguments
141            .iter()
142            .find(|item| item.0.node == name)
143            .map(|item| &item.1)
144    }
145}
146
147/// A GraphQL directive, such as `@deprecated(reason: "Use the other field")`.
148///
149/// [Reference](https://spec.graphql.org/October2021/#Directive).
150#[derive(Debug, Clone, Serialize, Deserialize)]
151pub struct Directive {
152    /// The name of the directive.
153    pub name: Positioned<Name>,
154    /// The arguments to the directive.
155    pub arguments: Vec<(Positioned<Name>, Positioned<Value>)>,
156}
157
158impl Directive {
159    /// Attempt to convert this `Directive` into a `ConstDirective`.
160    #[must_use]
161    pub fn into_const(self) -> Option<ConstDirective> {
162        Some(ConstDirective {
163            name: self.name,
164            arguments: self
165                .arguments
166                .into_iter()
167                .map(|(name, value)| {
168                    Some((name, Positioned::new(value.node.into_const()?, value.pos)))
169                })
170                .collect::<Option<_>>()?,
171        })
172    }
173
174    /// Get the argument with the given name.
175    #[must_use]
176    pub fn get_argument(&self, name: &str) -> Option<&Positioned<Value>> {
177        self.arguments
178            .iter()
179            .find(|item| item.0.node == name)
180            .map(|item| &item.1)
181    }
182}