bluejay_parser/ast/executable/
executable_document.rs

1use 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}