bluejay_parser/ast/executable/
field.rs1use crate::ast::executable::SelectionSet;
2use crate::ast::{
3 DepthLimiter, FromTokens, IsMatch, ParseError, Tokens, VariableArguments, VariableDirectives,
4};
5use crate::lexical_token::{Name, PunctuatorType};
6use crate::{HasSpan, Span};
7use std::cmp::{Eq, PartialEq};
8use std::hash::{Hash, Hasher};
9
10#[derive(Debug)]
11pub struct Field<'a> {
12 alias: Option<Name<'a>>,
13 name: Name<'a>,
14 arguments: Option<VariableArguments<'a>>,
15 directives: Option<VariableDirectives<'a>>,
16 selection_set: Option<SelectionSet<'a>>,
17 span: Span,
18}
19
20impl<'a> FromTokens<'a> for Field<'a> {
21 #[inline]
22 fn from_tokens(
23 tokens: &mut impl Tokens<'a>,
24 depth_limiter: DepthLimiter,
25 ) -> Result<Self, ParseError> {
26 let first_name = tokens.expect_name()?;
29 let (alias, name) = if tokens.next_if_punctuator(PunctuatorType::Colon).is_some() {
30 let name = tokens.expect_name()?;
31 (Some(first_name), name)
32 } else {
33 (None, first_name)
34 };
35 let arguments = if VariableArguments::is_match(tokens) {
36 Some(VariableArguments::from_tokens(
37 tokens,
38 depth_limiter.bump()?,
39 )?)
40 } else {
41 None
42 };
43 let directives = if VariableDirectives::is_match(tokens) {
44 Some(VariableDirectives::from_tokens(
45 tokens,
46 depth_limiter.bump()?,
47 )?)
48 } else {
49 None
50 };
51 let selection_set = if SelectionSet::is_match(tokens) {
52 Some(SelectionSet::from_tokens(tokens, depth_limiter.bump()?)?)
53 } else {
54 None
55 };
56 let start_span = alias.as_ref().unwrap_or(&name).span();
57 let directives_span = directives.as_ref().and_then(|directives| directives.span());
58 let end_span = if let Some(selection_set) = &selection_set {
59 selection_set.span()
60 } else if let Some(directive_span) = directives_span {
61 directive_span
62 } else if let Some(arguments) = &arguments {
63 arguments.span()
64 } else {
65 name.span()
66 };
67 let span = start_span.merge(end_span);
68 Ok(Self {
69 alias,
70 name,
71 arguments,
72 directives,
73 selection_set,
74 span,
75 })
76 }
77}
78
79impl<'a> IsMatch<'a> for Field<'a> {
80 #[inline]
81 fn is_match(tokens: &mut impl Tokens<'a>) -> bool {
82 tokens.peek_name(0).is_some()
83 }
84}
85
86impl<'a> Field<'a> {
87 pub fn response_key(&self) -> &str {
88 if let Some(alias) = &self.alias {
89 alias.as_ref()
90 } else {
91 self.name.as_ref()
92 }
93 }
94
95 pub fn name(&self) -> &Name<'a> {
96 &self.name
97 }
98
99 pub fn arguments(&self) -> Option<&VariableArguments<'_>> {
100 self.arguments.as_ref()
101 }
102
103 pub fn selection_set(&self) -> Option<&SelectionSet<'_>> {
104 self.selection_set.as_ref()
105 }
106}
107
108impl<'a> bluejay_core::executable::Field for Field<'a> {
109 type Arguments = VariableArguments<'a>;
110 type Directives = VariableDirectives<'a>;
111 type SelectionSet = SelectionSet<'a>;
112
113 fn alias(&self) -> Option<&str> {
114 self.alias.as_ref().map(|name| name.as_ref())
115 }
116
117 fn name(&self) -> &str {
118 self.name.as_ref()
119 }
120
121 fn arguments(&self) -> Option<&Self::Arguments> {
122 self.arguments.as_ref()
123 }
124
125 fn directives(&self) -> Option<&Self::Directives> {
126 self.directives.as_ref()
127 }
128
129 fn selection_set(&self) -> Option<&Self::SelectionSet> {
130 self.selection_set.as_ref()
131 }
132}
133
134impl HasSpan for Field<'_> {
135 fn span(&self) -> &Span {
136 &self.span
137 }
138}
139
140impl Hash for Field<'_> {
141 fn hash<H: Hasher>(&self, state: &mut H) {
142 self.span().hash(state);
143 }
144}
145
146impl PartialEq for Field<'_> {
147 fn eq(&self, other: &Self) -> bool {
148 self.span() == other.span()
149 }
150}
151
152impl Eq for Field<'_> {}