bluejay_parser/ast/executable/
operation_definition.rs1use 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}