bluejay_validator/executable/document/
rule.rs

1use crate::executable::{
2    document::{Path, Visitor},
3    Cache,
4};
5use bluejay_core::definition::{DirectiveLocation, SchemaDefinition, TypeDefinitionReference};
6use bluejay_core::executable::ExecutableDocument;
7use std::marker::PhantomData;
8
9pub trait Rule<'a, E: ExecutableDocument, S: SchemaDefinition>: Visitor<'a, E, S> {
10    type Error;
11    type Errors: Iterator<Item = Self::Error>;
12
13    fn into_errors(self) -> Self::Errors;
14}
15
16macro_rules! impl_rule {
17    ($n:literal) => {
18        seq_macro::seq!(N in 0..$n {
19            #[warn(clippy::missing_trait_methods)]
20            impl<'a, E: ExecutableDocument, S: SchemaDefinition, ER, #(T~N: Rule<'a, E, S, Error = ER>,)*> Rule<'a, E, S> for (#(T~N,)*) {
21                type Error = ER;
22                type Errors = #(std::iter::Chain<)* std::iter::Empty<ER> #(, <T~N as Rule<'a, E, S>>::Errors>)*;
23
24                fn into_errors(self) -> Self::Errors {
25                    std::iter::empty() #(.chain(self.N.into_errors()))*
26                }
27            }
28        });
29    }
30}
31
32seq_macro::seq!(N in 2..=10 {
33    impl_rule!(N);
34});
35
36impl_rule!(26);
37
38pub struct RuleErrorAdapter<R, ER> {
39    rule: R,
40    error: PhantomData<ER>,
41}
42
43#[warn(clippy::missing_trait_methods)]
44impl<'a, E: ExecutableDocument, S: SchemaDefinition, R: Rule<'a, E, S>, ER> Visitor<'a, E, S>
45    for RuleErrorAdapter<R, ER>
46{
47    fn new(
48        executable_document: &'a E,
49        schema_definition: &'a S,
50        cache: &'a Cache<'a, E, S>,
51    ) -> Self {
52        Self {
53            rule: R::new(executable_document, schema_definition, cache),
54            error: PhantomData,
55        }
56    }
57
58    fn visit_operation_definition(&mut self, operation_definition: &'a E::OperationDefinition) {
59        self.rule.visit_operation_definition(operation_definition);
60    }
61
62    fn visit_selection_set(
63        &mut self,
64        selection_set: &'a E::SelectionSet,
65        r#type: TypeDefinitionReference<'a, S::TypeDefinition>,
66    ) {
67        self.rule.visit_selection_set(selection_set, r#type);
68    }
69
70    fn visit_field(
71        &mut self,
72        field: &'a E::Field,
73        field_definition: &'a S::FieldDefinition,
74        path: &Path<'a, E>,
75    ) {
76        self.rule.visit_field(field, field_definition, path);
77    }
78
79    fn visit_const_directive(
80        &mut self,
81        directive: &'a E::Directive<true>,
82        location: DirectiveLocation,
83    ) {
84        self.rule.visit_const_directive(directive, location);
85    }
86
87    fn visit_variable_directive(
88        &mut self,
89        directive: &'a E::Directive<false>,
90        location: DirectiveLocation,
91    ) {
92        self.rule.visit_variable_directive(directive, location);
93    }
94
95    fn visit_const_directives(
96        &mut self,
97        directives: &'a E::Directives<true>,
98        location: DirectiveLocation,
99    ) {
100        self.rule.visit_const_directives(directives, location);
101    }
102
103    fn visit_variable_directives(
104        &mut self,
105        directives: &'a E::Directives<false>,
106        location: DirectiveLocation,
107    ) {
108        self.rule.visit_variable_directives(directives, location);
109    }
110
111    fn visit_fragment_definition(&mut self, fragment_definition: &'a E::FragmentDefinition) {
112        self.rule.visit_fragment_definition(fragment_definition);
113    }
114
115    fn visit_inline_fragment(
116        &mut self,
117        inline_fragment: &'a E::InlineFragment,
118        scoped_type: TypeDefinitionReference<'a, S::TypeDefinition>,
119    ) {
120        self.rule
121            .visit_inline_fragment(inline_fragment, scoped_type);
122    }
123
124    fn visit_fragment_spread(
125        &mut self,
126        fragment_spread: &'a E::FragmentSpread,
127        scoped_type: TypeDefinitionReference<'a, S::TypeDefinition>,
128        path: &Path<'a, E>,
129    ) {
130        self.rule
131            .visit_fragment_spread(fragment_spread, scoped_type, path);
132    }
133
134    fn visit_const_argument(
135        &mut self,
136        argument: &'a E::Argument<true>,
137        input_value_definition: &'a S::InputValueDefinition,
138    ) {
139        self.rule
140            .visit_const_argument(argument, input_value_definition);
141    }
142
143    fn visit_variable_argument(
144        &mut self,
145        argument: &'a E::Argument<false>,
146        input_value_definition: &'a S::InputValueDefinition,
147        path: &Path<'a, E>,
148    ) {
149        self.rule
150            .visit_variable_argument(argument, input_value_definition, path)
151    }
152
153    fn visit_variable_definition(&mut self, variable_definition: &'a E::VariableDefinition) {
154        self.rule.visit_variable_definition(variable_definition);
155    }
156
157    fn visit_variable_definitions(&mut self, variable_definitions: &'a E::VariableDefinitions) {
158        self.rule.visit_variable_definitions(variable_definitions);
159    }
160}
161
162#[warn(clippy::missing_trait_methods)]
163impl<
164        'a,
165        E: ExecutableDocument,
166        S: SchemaDefinition,
167        R: Rule<'a, E, S>,
168        ER: From<<R as Rule<'a, E, S>>::Error>,
169    > Rule<'a, E, S> for RuleErrorAdapter<R, ER>
170{
171    type Error = ER;
172    type Errors =
173        std::iter::Map<<R as Rule<'a, E, S>>::Errors, fn(<R as Rule<'a, E, S>>::Error) -> ER>;
174
175    fn into_errors(self) -> Self::Errors {
176        self.rule.into_errors().map(ER::from)
177    }
178}