1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
mod all_variable_usages_allowed;
mod all_variable_uses_defined;
mod all_variables_used;
mod argument_names;
mod argument_uniqueness;
mod directives_are_defined;
mod directives_are_in_valid_locations;
mod directives_are_unique_per_location;
mod field_selection_merging;
mod field_selections;
mod fragment_name_uniqueness;
mod fragment_spread_is_possible;
mod fragment_spread_target_defined;
mod fragment_spread_type_exists;
mod fragment_spreads_must_not_form_cycles;
mod fragments_must_be_used;
mod fragments_on_composite_types;
mod leaf_field_selections;
mod lone_anonymous_operation;
mod named_operation_name_uniqueness;
mod operation_type_is_defined;
mod required_arguments;
mod subscription_operation_single_root_field;
mod value_is_valid;
mod variable_uniqueness;
mod variables_are_input_types;

pub use all_variable_usages_allowed::AllVariableUsagesAllowed;
pub use all_variable_uses_defined::AllVariableUsesDefined;
pub use all_variables_used::AllVariablesUsed;
pub use argument_names::ArgumentNames;
pub use argument_uniqueness::ArgumentUniqueness;
pub use directives_are_defined::DirectivesAreDefined;
pub use directives_are_in_valid_locations::DirectivesAreInValidLocations;
pub use directives_are_unique_per_location::DirectivesAreUniquePerLocation;
pub use field_selection_merging::FieldSelectionMerging;
pub use field_selections::FieldSelections;
pub use fragment_name_uniqueness::FragmentNameUniqueness;
pub use fragment_spread_is_possible::FragmentSpreadIsPossible;
pub use fragment_spread_target_defined::FragmentSpreadTargetDefined;
pub use fragment_spread_type_exists::FragmentSpreadTypeExists;
pub use fragment_spreads_must_not_form_cycles::FragmentSpreadsMustNotFormCycles;
pub use fragments_must_be_used::FragmentsMustBeUsed;
pub use fragments_on_composite_types::FragmentsOnCompositeTypes;
pub use leaf_field_selections::LeafFieldSelections;
pub use lone_anonymous_operation::LoneAnonymousOperation;
pub use named_operation_name_uniqueness::NamedOperationNameUniqueness;
pub use operation_type_is_defined::OperationTypeIsDefined;
pub use required_arguments::RequiredArguments;
pub use subscription_operation_single_root_field::SubscriptionOperationSingleRootField;
pub use value_is_valid::ValueIsValid;
pub use variable_uniqueness::VariableUniqueness;
pub use variables_are_input_types::VariablesAreInputTypes;

/// Combines multiple rules into a single rule.
/// Args:
/// 1. Name of the resulting struct
/// 2. Name of the error type returned by the rule.
///    Must accept generic lifetime, executable document, and schema definition. e.g. pass Error for `Error<'a, E, S>`.
/// 3. Rules, a comma-separated list of types accepting generic lifetime, executable document, and schema definition.
///    Must implement Rule<'a, E, S>. Must be wrapped in square brackets. e.g. `[FirstRule, SecondRule]`.
///    The `Error` type of each rule must be convertable to the error type of the new rule via `Into::into`.
#[macro_export]
macro_rules! combine_executable_rules {
    ( $name:ty, $err:ty, [$( $rule:ty ),* $(,)?] $(,)? ) => {
        paste::paste! {
            pub struct $name<'a, E: bluejay_core::executable::ExecutableDocument, S: bluejay_core::definition::SchemaDefinition> {
                $([<$rule:snake>]: $rule<'a, E, S>,)*
            }

            impl<'a, E: bluejay_core::executable::ExecutableDocument + 'a, S: bluejay_core::definition::SchemaDefinition + 'a> $crate::executable::Rule<'a, E, S> for $name<'a, E, S> {
                type Error = $err<'a, E, S>;

                fn new(executable_document: &'a E, schema_definition: &'a S, cache: &'a $crate::executable::Cache<'a, E, S>) -> Self {
                    Self {
                        $([<$rule:snake>]: $rule::new(executable_document, schema_definition, cache),)*
                    }
                }
            }

            impl<'a, E: bluejay_core::executable::ExecutableDocument + 'a, S: bluejay_core::definition::SchemaDefinition + 'a> IntoIterator for $name<'a, E, S> {
                type Item = $err<'a, E, S>;
                type IntoIter = $crate::chain_types!($(std::iter::Map<<$rule<'a, E, S> as IntoIterator>::IntoIter, fn(<$rule<'a, E, S> as $crate::executable::Rule<'a, E, S>>::Error) -> $err<'a, E, S>>),*);

                fn into_iter(self) -> Self::IntoIter {
                    $crate::chain_iters!($(self.[<$rule:snake>].into_iter().map(Into::into as fn(<$rule<'a, E, S> as $crate::executable::Rule<'a, E, S>>::Error) -> $err<'a, E, S>)),*)
                }
            }

            impl<'a, E: bluejay_core::executable::ExecutableDocument, S: bluejay_core::definition::SchemaDefinition> $crate::executable::Visitor<'a, E, S> for $name<'a, E, S> {
                fn visit_operation_definition(&mut self, operation_definition: &'a E::OperationDefinition) {
                    $(self.[<$rule:snake>].visit_operation_definition(operation_definition);)*
                }

                fn visit_selection_set(
                    &mut self,
                    selection_set: &'a E::SelectionSet,
                    r#type: bluejay_core::definition::TypeDefinitionReference<'a, S::TypeDefinition>,
                ) {
                    $(self.[<$rule:snake>].visit_selection_set(selection_set, r#type);)*
                }

                fn visit_field(&mut self, field: &'a E::Field, field_definition: &'a S::FieldDefinition, path: &$crate::executable::Path<'a, E>) {
                    $(self.[<$rule:snake>].visit_field(field, field_definition, path);)*
                }

                fn visit_const_directive(&mut self, directive: &'a E::Directive<true>, location: bluejay_core::definition::DirectiveLocation) {
                    $(self.[<$rule:snake>].visit_const_directive(directive, location);)*
                }

                fn visit_variable_directive(&mut self, directive: &'a E::Directive<false>, location: bluejay_core::definition::DirectiveLocation) {
                    $(self.[<$rule:snake>].visit_variable_directive(directive, location);)*
                }

                fn visit_const_directives(
                    &mut self,
                    directives: &'a E::Directives<true>,
                    location: bluejay_core::definition::DirectiveLocation,
                ) {
                    $(self.[<$rule:snake>].visit_const_directives(directives, location);)*
                }

                fn visit_variable_directives(
                    &mut self,
                    directives: &'a E::Directives<false>,
                    location: bluejay_core::definition::DirectiveLocation,
                ) {
                    $(self.[<$rule:snake>].visit_variable_directives(directives, location);)*
                }

                fn visit_fragment_definition(&mut self, fragment_definition: &'a E::FragmentDefinition) {
                    $(self.[<$rule:snake>].visit_fragment_definition(fragment_definition);)*
                }

                fn visit_inline_fragment(
                    &mut self,
                    inline_fragment: &'a E::InlineFragment,
                    scoped_type: bluejay_core::definition::TypeDefinitionReference<'a, S::TypeDefinition>,
                ) {
                    $(self.[<$rule:snake>].visit_inline_fragment(inline_fragment, scoped_type);)*
                }

                fn visit_fragment_spread(
                    &mut self,
                    fragment_spread: &'a E::FragmentSpread,
                    scoped_type: bluejay_core::definition::TypeDefinitionReference<'a, S::TypeDefinition>,
                    path: &$crate::executable::Path<'a, E>,
                ) {
                    $(self.[<$rule:snake>].visit_fragment_spread(fragment_spread, scoped_type, path);)*
                }

                fn visit_const_argument(
                    &mut self,
                    argument: &'a E::Argument<true>,
                    input_value_definition: &'a S::InputValueDefinition,
                ) {
                    $(self.[<$rule:snake>].visit_const_argument(argument, input_value_definition);)*
                }

                fn visit_variable_argument(
                    &mut self,
                    argument: &'a E::Argument<false>,
                    input_value_definition: &'a S::InputValueDefinition,
                    path: &$crate::executable::Path<'a, E>,
                ) {
                    $(self.[<$rule:snake>].visit_variable_argument(argument, input_value_definition, path);)*
                }

                fn visit_variable_definition(&mut self, variable_definition: &'a E::VariableDefinition) {
                    $(self.[<$rule:snake>].visit_variable_definition(variable_definition);)*
                }

                fn visit_variable_definitions(&mut self, variable_definitions: &'a E::VariableDefinitions) {
                    $(self.[<$rule:snake>].visit_variable_definitions(variable_definitions);)*
                }
            }
        }
    };
}

combine_executable_rules!(
    BuiltinRules,
    crate::executable::Error,
    [
        NamedOperationNameUniqueness,
        LoneAnonymousOperation,
        SubscriptionOperationSingleRootField,
        FieldSelections,
        FieldSelectionMerging,
        OperationTypeIsDefined,
        LeafFieldSelections,
        ArgumentNames,
        ArgumentUniqueness,
        RequiredArguments,
        FragmentNameUniqueness,
        FragmentSpreadTypeExists,
        FragmentsOnCompositeTypes,
        FragmentsMustBeUsed,
        FragmentSpreadTargetDefined,
        FragmentSpreadsMustNotFormCycles,
        FragmentSpreadIsPossible,
        ValueIsValid,
        DirectivesAreDefined,
        DirectivesAreInValidLocations,
        DirectivesAreUniquePerLocation,
        VariableUniqueness,
        VariablesAreInputTypes,
        AllVariableUsesDefined,
        AllVariablesUsed,
        AllVariableUsagesAllowed,
    ],
);