2020-09-08 08:21:27 +00:00
|
|
|
//! Executable document-related GraphQL types.
|
|
|
|
|
|
|
|
use super::*;
|
2020-10-11 12:24:31 +00:00
|
|
|
use async_graphql_value::{ConstValue, Name, Value};
|
2020-09-08 08:21:27 +00:00
|
|
|
|
|
|
|
/// An executable GraphQL file or request string.
|
|
|
|
///
|
|
|
|
/// [Reference](https://spec.graphql.org/June2018/#ExecutableDocument).
|
|
|
|
#[derive(Debug, Clone)]
|
|
|
|
pub struct ExecutableDocument {
|
2020-09-22 18:59:48 +00:00
|
|
|
/// The operations of the document.
|
|
|
|
pub operations: DocumentOperations,
|
|
|
|
/// The fragments of the document.
|
|
|
|
pub fragments: HashMap<Name, Positioned<FragmentDefinition>>,
|
2020-09-08 08:21:27 +00:00
|
|
|
}
|
|
|
|
|
2020-09-22 18:59:48 +00:00
|
|
|
/// The operations of a GraphQL document.
|
|
|
|
///
|
|
|
|
/// There is either one anonymous operation or many named operations.
|
|
|
|
#[derive(Debug, Clone)]
|
|
|
|
pub enum DocumentOperations {
|
|
|
|
/// The document contains a single anonymous operation.
|
|
|
|
Single(Positioned<OperationDefinition>),
|
|
|
|
/// The document contains many named operations.
|
|
|
|
Multiple(HashMap<Name, Positioned<OperationDefinition>>),
|
|
|
|
}
|
2020-09-08 08:21:27 +00:00
|
|
|
|
2020-09-22 18:59:48 +00:00
|
|
|
impl DocumentOperations {
|
|
|
|
/// Iterate over the operations of the document.
|
|
|
|
#[must_use]
|
|
|
|
pub fn iter(&self) -> OperationsIter<'_> {
|
|
|
|
OperationsIter(match self {
|
|
|
|
Self::Single(op) => OperationsIterInner::Single(Some(op)),
|
|
|
|
Self::Multiple(ops) => OperationsIterInner::Multiple(ops.iter()),
|
2020-09-08 08:21:27 +00:00
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-09-22 18:59:48 +00:00
|
|
|
// TODO: This is not implemented as I would like to later implement IntoIterator for
|
|
|
|
// DocumentOperations (not a reference) without having a breaking change.
|
|
|
|
//
|
|
|
|
//impl<'a> IntoIterator for &'a DocumentOperations {
|
|
|
|
// type Item = &'a Positioned<OperationDefinition>;
|
|
|
|
// type IntoIter = OperationsIter<'a>;
|
|
|
|
//
|
|
|
|
// fn into_iter(self) -> Self::IntoIter {
|
|
|
|
// self.iter()
|
|
|
|
// }
|
|
|
|
//}
|
2020-09-08 08:21:27 +00:00
|
|
|
|
2020-09-22 18:59:48 +00:00
|
|
|
/// An iterator over the operations of a document.
|
2020-09-08 08:21:27 +00:00
|
|
|
#[derive(Debug, Clone)]
|
2020-09-22 18:59:48 +00:00
|
|
|
pub struct OperationsIter<'a>(OperationsIterInner<'a>);
|
2020-09-08 08:21:27 +00:00
|
|
|
|
2020-09-22 18:59:48 +00:00
|
|
|
impl<'a> Iterator for OperationsIter<'a> {
|
|
|
|
type Item = (Option<&'a Name>, &'a Positioned<OperationDefinition>);
|
|
|
|
|
|
|
|
fn next(&mut self) -> Option<Self::Item> {
|
|
|
|
match &mut self.0 {
|
|
|
|
OperationsIterInner::Single(op) => op.take().map(|op| (None, op)),
|
|
|
|
OperationsIterInner::Multiple(iter) => iter.next().map(|(name, op)| (Some(name), op)),
|
2020-09-08 08:21:27 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-09-22 18:59:48 +00:00
|
|
|
fn size_hint(&self) -> (usize, Option<usize>) {
|
|
|
|
let size = self.len();
|
|
|
|
(size, Some(size))
|
2020-09-08 08:21:27 +00:00
|
|
|
}
|
2020-09-22 18:59:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a> std::iter::FusedIterator for OperationsIter<'a> {}
|
|
|
|
|
|
|
|
impl<'a> ExactSizeIterator for OperationsIter<'a> {
|
|
|
|
fn len(&self) -> usize {
|
|
|
|
match &self.0 {
|
|
|
|
OperationsIterInner::Single(opt) => {
|
|
|
|
if opt.is_some() {
|
|
|
|
1
|
|
|
|
} else {
|
|
|
|
0
|
|
|
|
}
|
|
|
|
}
|
|
|
|
OperationsIterInner::Multiple(iter) => iter.len(),
|
2020-09-08 08:21:27 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-09-22 18:59:48 +00:00
|
|
|
#[derive(Debug, Clone)]
|
|
|
|
enum OperationsIterInner<'a> {
|
|
|
|
Single(Option<&'a Positioned<OperationDefinition>>),
|
|
|
|
Multiple(hash_map::Iter<'a, Name, Positioned<OperationDefinition>>),
|
|
|
|
}
|
|
|
|
|
2020-09-08 08:21:27 +00:00
|
|
|
/// A GraphQL operation, such as `mutation($content:String!) { makePost(content: $content) { id } }`.
|
|
|
|
///
|
|
|
|
/// [Reference](https://spec.graphql.org/June2018/#OperationDefinition).
|
|
|
|
#[derive(Debug, Clone)]
|
|
|
|
pub struct OperationDefinition {
|
|
|
|
/// The type of operation.
|
|
|
|
pub ty: OperationType,
|
|
|
|
/// The variable definitions.
|
|
|
|
pub variable_definitions: Vec<Positioned<VariableDefinition>>,
|
|
|
|
/// The operation's directives.
|
|
|
|
pub directives: Vec<Positioned<Directive>>,
|
|
|
|
/// The operation's selection set.
|
|
|
|
pub selection_set: Positioned<SelectionSet>,
|
|
|
|
}
|
|
|
|
|
|
|
|
/// A variable definition inside a list of variable definitions, for example `$name:String!`.
|
|
|
|
///
|
|
|
|
/// [Reference](https://spec.graphql.org/June2018/#VariableDefinition).
|
|
|
|
#[derive(Debug, Clone)]
|
|
|
|
pub struct VariableDefinition {
|
|
|
|
/// The name of the variable, without the preceding `$`.
|
|
|
|
pub name: Positioned<Name>,
|
|
|
|
/// The type of the variable.
|
|
|
|
pub var_type: Positioned<Type>,
|
|
|
|
/// The optional default value of the variable.
|
|
|
|
pub default_value: Option<Positioned<ConstValue>>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl VariableDefinition {
|
|
|
|
/// Get the default value of the variable; this is `default_value` if it is present,
|
|
|
|
/// `Value::Null` if it is nullable and `None` otherwise.
|
|
|
|
#[must_use]
|
|
|
|
pub fn default_value(&self) -> Option<&ConstValue> {
|
|
|
|
self.default_value
|
|
|
|
.as_ref()
|
|
|
|
.map(|value| &value.node)
|
|
|
|
.or_else(|| {
|
|
|
|
if self.var_type.node.nullable {
|
|
|
|
Some(&ConstValue::Null)
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// A set of fields to be selected, for example `{ name age }`.
|
|
|
|
///
|
|
|
|
/// [Reference](https://spec.graphql.org/June2018/#SelectionSet).
|
|
|
|
#[derive(Debug, Default, Clone)]
|
|
|
|
pub struct SelectionSet {
|
|
|
|
/// The fields to be selected.
|
|
|
|
pub items: Vec<Positioned<Selection>>,
|
|
|
|
}
|
|
|
|
|
|
|
|
/// A part of an object to be selected; a single field, a fragment spread or an inline fragment.
|
|
|
|
///
|
|
|
|
/// [Reference](https://spec.graphql.org/June2018/#Selection).
|
|
|
|
#[derive(Debug, Clone)]
|
|
|
|
pub enum Selection {
|
|
|
|
/// Select a single field, such as `name` or `weightKilos: weight(unit: KILOGRAMS)`.
|
|
|
|
Field(Positioned<Field>),
|
|
|
|
/// Select using a fragment.
|
|
|
|
FragmentSpread(Positioned<FragmentSpread>),
|
|
|
|
/// Select using an inline fragment.
|
|
|
|
InlineFragment(Positioned<InlineFragment>),
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Selection {
|
|
|
|
/// Get a reference to the directives of the selection.
|
|
|
|
#[must_use]
|
|
|
|
pub fn directives(&self) -> &Vec<Positioned<Directive>> {
|
|
|
|
match self {
|
|
|
|
Self::Field(field) => &field.node.directives,
|
|
|
|
Self::FragmentSpread(spread) => &spread.node.directives,
|
|
|
|
Self::InlineFragment(fragment) => &fragment.node.directives,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/// Get a mutable reference to the directives of the selection.
|
|
|
|
#[must_use]
|
|
|
|
pub fn directives_mut(&mut self) -> &mut Vec<Positioned<Directive>> {
|
|
|
|
match self {
|
|
|
|
Self::Field(field) => &mut field.node.directives,
|
|
|
|
Self::FragmentSpread(spread) => &mut spread.node.directives,
|
|
|
|
Self::InlineFragment(fragment) => &mut fragment.node.directives,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// A field being selected on an object, such as `name` or `weightKilos: weight(unit: KILOGRAMS)`.
|
|
|
|
///
|
|
|
|
/// [Reference](https://spec.graphql.org/June2018/#Field).
|
|
|
|
#[derive(Debug, Clone)]
|
|
|
|
pub struct Field {
|
|
|
|
/// The optional field alias.
|
|
|
|
pub alias: Option<Positioned<Name>>,
|
|
|
|
/// The name of the field.
|
|
|
|
pub name: Positioned<Name>,
|
|
|
|
/// The arguments to the field, empty if no arguments are provided.
|
|
|
|
pub arguments: Vec<(Positioned<Name>, Positioned<Value>)>,
|
|
|
|
/// The directives in the field selector.
|
|
|
|
pub directives: Vec<Positioned<Directive>>,
|
|
|
|
/// The subfields being selected in this field, if it is an object. Empty if no fields are
|
|
|
|
/// being selected.
|
|
|
|
pub selection_set: Positioned<SelectionSet>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Field {
|
|
|
|
/// Get the response key of the field. This is the alias if present and the name otherwise.
|
|
|
|
#[must_use]
|
|
|
|
pub fn response_key(&self) -> &Positioned<Name> {
|
|
|
|
self.alias.as_ref().unwrap_or(&self.name)
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Get the value of the argument with the specified 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)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// A fragment selector, such as `... userFields`.
|
|
|
|
///
|
|
|
|
/// [Reference](https://spec.graphql.org/June2018/#FragmentSpread).
|
|
|
|
#[derive(Debug, Clone)]
|
|
|
|
pub struct FragmentSpread {
|
|
|
|
/// The name of the fragment being selected.
|
|
|
|
pub fragment_name: Positioned<Name>,
|
|
|
|
/// The directives in the fragment selector.
|
|
|
|
pub directives: Vec<Positioned<Directive>>,
|
|
|
|
}
|
|
|
|
|
|
|
|
/// An inline fragment selector, such as `... on User { name }`.
|
|
|
|
///
|
|
|
|
/// [Reference](https://spec.graphql.org/June2018/#InlineFragment).
|
|
|
|
#[derive(Debug, Clone)]
|
|
|
|
pub struct InlineFragment {
|
|
|
|
/// The type condition.
|
|
|
|
pub type_condition: Option<Positioned<TypeCondition>>,
|
|
|
|
/// The directives in the inline fragment.
|
|
|
|
pub directives: Vec<Positioned<Directive>>,
|
|
|
|
/// The selected fields of the fragment.
|
|
|
|
pub selection_set: Positioned<SelectionSet>,
|
|
|
|
}
|
|
|
|
|
|
|
|
/// The definition of a fragment, such as `fragment userFields on User { name age }`.
|
|
|
|
///
|
|
|
|
/// [Reference](https://spec.graphql.org/June2018/#FragmentDefinition).
|
|
|
|
#[derive(Debug, Clone)]
|
|
|
|
pub struct FragmentDefinition {
|
|
|
|
/// The type this fragment operates on.
|
|
|
|
pub type_condition: Positioned<TypeCondition>,
|
|
|
|
/// Directives in the fragment.
|
|
|
|
pub directives: Vec<Positioned<Directive>>,
|
|
|
|
/// The fragment's selection set.
|
|
|
|
pub selection_set: Positioned<SelectionSet>,
|
|
|
|
}
|
|
|
|
|
|
|
|
/// A type a fragment can apply to (`on` followed by the type).
|
|
|
|
///
|
|
|
|
/// [Reference](https://spec.graphql.org/June2018/#TypeCondition).
|
|
|
|
#[derive(Debug, Clone)]
|
|
|
|
pub struct TypeCondition {
|
|
|
|
/// The type this fragment applies to.
|
|
|
|
pub on: Positioned<Name>,
|
|
|
|
}
|