Skip to main content

bluejay_parser/ast/executable/
operation_definition.rs

1use crate::ast::executable::{SelectionSet, VariableDefinitions};
2use crate::ast::{
3    DepthLimiter, FromTokens, IsMatch, OperationType, ParseError, Tokens, TryFromTokens,
4    VariableDirectives,
5};
6use crate::lexical_token::{Name, StringValue};
7use crate::{HasSpan, Span};
8use bluejay_core::{
9    executable::{OperationDefinition as CoreOperationDefinition, OperationDefinitionReference},
10    Indexable,
11};
12
13#[derive(Debug)]
14pub enum OperationDefinition<'a> {
15    Explicit(ExplicitOperationDefinition<'a>),
16    Implicit(ImplicitOperationDefinition<'a>),
17}
18
19impl<'a> OperationDefinition<'a> {
20    pub fn selection_set(&self) -> &SelectionSet<'a> {
21        match self {
22            Self::Explicit(e) => &e.selection_set,
23            Self::Implicit(i) => &i.selection_set,
24        }
25    }
26}
27
28impl Indexable for OperationDefinition<'_> {
29    type Id = Span;
30
31    fn id(&self) -> &Self::Id {
32        self.span()
33    }
34}
35
36impl<'a> CoreOperationDefinition for OperationDefinition<'a> {
37    type ExplicitOperationDefinition = ExplicitOperationDefinition<'a>;
38    type ImplicitOperationDefinition = ImplicitOperationDefinition<'a>;
39
40    fn as_ref(&self) -> OperationDefinitionReference<'_, Self> {
41        match self {
42            Self::Explicit(e) => OperationDefinitionReference::Explicit(e),
43            Self::Implicit(i) => OperationDefinitionReference::Implicit(i),
44        }
45    }
46}
47
48impl<'a> FromTokens<'a> for OperationDefinition<'a> {
49    #[inline]
50    fn from_tokens(
51        tokens: &mut impl Tokens<'a>,
52        depth_limiter: DepthLimiter,
53    ) -> Result<Self, ParseError> {
54        let description = tokens.next_if_string_value();
55        if let Some(operation_type) = OperationType::try_from_tokens(tokens, depth_limiter.bump()?)?
56        {
57            let name = tokens.next_if_name();
58            let variable_definitions =
59                VariableDefinitions::try_from_tokens(tokens, depth_limiter.bump()?)?;
60            let directives = VariableDirectives::try_from_tokens(tokens, depth_limiter.bump()?)?;
61            let selection_set = SelectionSet::from_tokens(tokens, depth_limiter.bump()?)?;
62            let span = if let Some(desc) = &description {
63                desc.span().merge(selection_set.span())
64            } else {
65                operation_type.span().merge(selection_set.span())
66            };
67            Ok(Self::Explicit(ExplicitOperationDefinition {
68                description,
69                operation_type,
70                name,
71                variable_definitions,
72                directives,
73                selection_set,
74                span,
75            }))
76        } else if description.is_none() {
77            if let Some(selection_set) =
78                SelectionSet::try_from_tokens(tokens, depth_limiter.bump()?)?
79            {
80                Ok(Self::Implicit(ImplicitOperationDefinition {
81                    selection_set,
82                }))
83            } else {
84                Err(tokens.unexpected_token())
85            }
86        } else {
87            Err(tokens.unexpected_token())
88        }
89    }
90}
91
92impl<'a> IsMatch<'a> for OperationDefinition<'a> {
93    #[inline]
94    fn is_match(tokens: &mut impl Tokens<'a>) -> bool {
95        OperationType::is_match(tokens)
96            || SelectionSet::is_match(tokens)
97            || (tokens.peek_string_value(0)
98                && bluejay_core::OperationType::POSSIBLE_VALUES
99                    .iter()
100                    .any(|value| tokens.peek_name_matches(1, value)))
101    }
102}
103
104impl HasSpan for OperationDefinition<'_> {
105    fn span(&self) -> &Span {
106        match self {
107            Self::Explicit(e) => e.span(),
108            Self::Implicit(i) => i.span(),
109        }
110    }
111}
112
113#[derive(Debug)]
114pub struct ExplicitOperationDefinition<'a> {
115    description: Option<StringValue<'a>>,
116    operation_type: OperationType,
117    name: Option<Name<'a>>,
118    variable_definitions: Option<VariableDefinitions<'a>>,
119    directives: Option<VariableDirectives<'a>>,
120    selection_set: SelectionSet<'a>,
121    span: Span,
122}
123
124impl<'a> bluejay_core::executable::ExplicitOperationDefinition for ExplicitOperationDefinition<'a> {
125    type VariableDefinitions = VariableDefinitions<'a>;
126    type Directives = VariableDirectives<'a>;
127    type SelectionSet = SelectionSet<'a>;
128
129    fn description(&self) -> Option<&str> {
130        self.description.as_ref().map(AsRef::as_ref)
131    }
132
133    fn operation_type(&self) -> bluejay_core::OperationType {
134        (&self.operation_type).into()
135    }
136
137    fn name(&self) -> Option<&str> {
138        self.name.as_ref().map(|name| name.as_ref())
139    }
140
141    fn variable_definitions(&self) -> Option<&Self::VariableDefinitions> {
142        self.variable_definitions.as_ref()
143    }
144
145    fn directives(&self) -> Option<&Self::Directives> {
146        self.directives.as_ref()
147    }
148
149    fn selection_set(&self) -> &Self::SelectionSet {
150        &self.selection_set
151    }
152}
153
154impl<'a> ExplicitOperationDefinition<'a> {
155    pub fn name(&self) -> Option<&Name<'a>> {
156        self.name.as_ref()
157    }
158
159    pub fn operation_type(&self) -> &OperationType {
160        &self.operation_type
161    }
162}
163
164impl HasSpan for ExplicitOperationDefinition<'_> {
165    fn span(&self) -> &Span {
166        &self.span
167    }
168}
169
170#[derive(Debug)]
171pub struct ImplicitOperationDefinition<'a> {
172    selection_set: SelectionSet<'a>,
173}
174
175impl<'a> bluejay_core::executable::ImplicitOperationDefinition for ImplicitOperationDefinition<'a> {
176    type SelectionSet = SelectionSet<'a>;
177
178    fn selection_set(&self) -> &Self::SelectionSet {
179        &self.selection_set
180    }
181}
182
183impl HasSpan for ImplicitOperationDefinition<'_> {
184    fn span(&self) -> &Span {
185        self.selection_set.span()
186    }
187}