bluejay_parser/ast/executable/
executable_document.rs1use crate::ast::executable::{
2 ExecutableDefinition, ExplicitOperationDefinition, Field, FragmentDefinition, FragmentSpread,
3 ImplicitOperationDefinition, InlineFragment, OperationDefinition, Selection, SelectionSet,
4 VariableDefinition, VariableDefinitions, VariableType,
5};
6use crate::ast::{
7 Argument, Arguments, Directive, Directives, Parse, ParseError, Tokens, TryFromTokens, Value,
8};
9use crate::Error;
10
11#[derive(Debug)]
12pub struct ExecutableDocument<'a> {
13 operation_definitions: Vec<OperationDefinition<'a>>,
14 fragment_definitions: Vec<FragmentDefinition<'a>>,
15}
16
17impl<'a> ExecutableDocument<'a> {
18 pub(crate) fn new(
19 operation_definitions: Vec<OperationDefinition<'a>>,
20 fragment_definitions: Vec<FragmentDefinition<'a>>,
21 ) -> Self {
22 Self {
23 operation_definitions,
24 fragment_definitions,
25 }
26 }
27
28 pub fn operation_definitions(&self) -> &[OperationDefinition<'a>] {
29 &self.operation_definitions
30 }
31
32 pub fn fragment_definitions(&self) -> &[FragmentDefinition<'a>] {
33 &self.fragment_definitions
34 }
35
36 #[inline]
37 fn is_empty(&self) -> bool {
38 self.operation_definitions.is_empty() && self.fragment_definitions.is_empty()
39 }
40}
41
42impl<'a> Parse<'a> for ExecutableDocument<'a> {
43 #[inline]
44 fn parse_from_tokens(mut tokens: impl Tokens<'a>) -> Result<Self, Vec<Error>> {
45 let mut instance: Self = Self::new(Vec::new(), Vec::new());
46 let mut errors = Vec::new();
47 let mut last_pass_had_error = false;
48
49 loop {
50 last_pass_had_error =
51 if let Some(res) = ExecutableDefinition::try_from_tokens(&mut tokens) {
52 match res {
53 Ok(ExecutableDefinition::Operation(operation_definition)) => {
54 instance.operation_definitions.push(operation_definition);
55 false
56 }
57 Ok(ExecutableDefinition::Fragment(fragment_definition)) => {
58 instance.fragment_definitions.push(fragment_definition);
59 false
60 }
61 Err(err) => {
62 if !last_pass_had_error {
63 errors.push(err);
64 }
65 true
66 }
67 }
68 } else if let Some(token) = tokens.next() {
69 if !last_pass_had_error {
70 errors.push(ParseError::UnexpectedToken { span: token.into() });
71 }
72 true
73 } else {
74 break;
75 }
76 }
77
78 let lex_errors = tokens.into_errors();
79
80 let errors = if lex_errors.is_empty() {
81 if errors.is_empty() && instance.is_empty() {
82 vec![ParseError::EmptyDocument.into()]
83 } else {
84 errors.into_iter().map(Into::into).collect()
85 }
86 } else {
87 lex_errors.into_iter().map(Into::into).collect()
88 };
89
90 if errors.is_empty() {
91 Ok(instance)
92 } else {
93 Err(errors)
94 }
95 }
96}
97
98impl<'a> bluejay_core::executable::ExecutableDocument for ExecutableDocument<'a> {
99 type Value<const CONST: bool> = Value<'a, CONST>;
100 type VariableType = VariableType<'a>;
101 type Argument<const CONST: bool> = Argument<'a, CONST>;
102 type Arguments<const CONST: bool> = Arguments<'a, CONST>;
103 type Directive<const CONST: bool> = Directive<'a, CONST>;
104 type Directives<const CONST: bool> = Directives<'a, CONST>;
105 type FragmentSpread = FragmentSpread<'a>;
106 type Field = Field<'a>;
107 type Selection = Selection<'a>;
108 type SelectionSet = SelectionSet<'a>;
109 type InlineFragment = InlineFragment<'a>;
110 type VariableDefinition = VariableDefinition<'a>;
111 type VariableDefinitions = VariableDefinitions<'a>;
112 type ExplicitOperationDefinition = ExplicitOperationDefinition<'a>;
113 type ImplicitOperationDefinition = ImplicitOperationDefinition<'a>;
114 type OperationDefinition = OperationDefinition<'a>;
115 type FragmentDefinition = FragmentDefinition<'a>;
116 type FragmentDefinitions<'b> = std::slice::Iter<'b, Self::FragmentDefinition> where Self: 'b;
117 type OperationDefinitions<'b> = std::slice::Iter<'b, Self::OperationDefinition> where Self: 'b;
118
119 fn operation_definitions(&self) -> Self::OperationDefinitions<'_> {
120 self.operation_definitions.iter()
121 }
122
123 fn fragment_definitions(&self) -> Self::FragmentDefinitions<'_> {
124 self.fragment_definitions.iter()
125 }
126}
127
128#[cfg(test)]
129mod tests {
130 use super::{ExecutableDocument, Parse};
131
132 #[test]
133 fn test_success() {
134 let document = r#"
135 {
136 dog {
137 ...fragmentOne
138 ...fragmentTwo
139 }
140 }
141
142 fragment fragmentOne on Dog {
143 name
144 }
145
146 fragment fragmentTwo on Dog {
147 owner {
148 name
149 }
150 }
151 "#;
152
153 let defs = ExecutableDocument::parse(document).unwrap();
154
155 assert_eq!(2, defs.fragment_definitions().len());
156 assert_eq!(1, defs.operation_definitions().len());
157 }
158}