bluejay_visibility/
schema_definition.rs

1use crate::{
2    ArgumentsDefinition, Cache, Directive, DirectiveDefinition, Directives, EnumTypeDefinition,
3    EnumValueDefinition, EnumValueDefinitions, FieldDefinition, FieldsDefinition,
4    InputFieldsDefinition, InputObjectTypeDefinition, InputType, InputValueDefinition,
5    InterfaceImplementation, InterfaceImplementations, InterfaceTypeDefinition,
6    ObjectTypeDefinition, OutputType, ScalarTypeDefinition, TypeDefinition, UnionMemberType,
7    UnionMemberTypes, UnionTypeDefinition, Warden,
8};
9use bluejay_core::definition::{
10    self, prelude::*, DirectiveLocation, HasDirectives, TypeDefinitionReference,
11};
12use bluejay_core::{AsIter, Directive as _};
13use elsa::FrozenMap;
14use once_cell::unsync::OnceCell;
15use std::collections::{
16    btree_map::{self, Entry},
17    BTreeMap,
18};
19
20#[derive(Debug)]
21pub enum SchemaDefinitionError<'a> {
22    QueryRootNotVisible,
23    NonUniqueTypeDefinitionName(&'a str),
24}
25
26pub struct SchemaDefinition<'a, S: definition::SchemaDefinition, W: Warden<SchemaDefinition = S>> {
27    cache: &'a Cache<'a, S, W>,
28    query: &'a ObjectTypeDefinition<'a, S, W>,
29    mutation: Option<&'a ObjectTypeDefinition<'a, S, W>>,
30    subscription: Option<&'a ObjectTypeDefinition<'a, S, W>>,
31    interface_implementors: FrozenMap<&'a str, Vec<&'a ObjectTypeDefinition<'a, S, W>>>,
32    type_definitions_and_directive_definitions:
33        OnceCell<TypeDefinitionsAndDirectiveDefinitions<'a, S, W>>,
34    directives: Option<Directives<'a, S, W>>,
35}
36
37impl<'a, S: definition::SchemaDefinition, W: Warden<SchemaDefinition = S>>
38    SchemaDefinition<'a, S, W>
39{
40    pub fn new(cache: &'a Cache<'a, S, W>) -> Result<Self, SchemaDefinitionError<'a>> {
41        let inner = cache.inner_schema_definition();
42        Ok(Self {
43            cache,
44            query: cache
45                .get_or_create_type_definition(TypeDefinitionReference::Object(inner.query()))
46                .ok_or(SchemaDefinitionError::QueryRootNotVisible)
47                .map(|td| {
48                    td.as_object()
49                        .expect("Error with internal handling of non-unique type names")
50                })?,
51            mutation: inner.mutation().and_then(|mutation| {
52                cache
53                    .get_or_create_type_definition(TypeDefinitionReference::Object(mutation))
54                    .map(|td| {
55                        td.as_object()
56                            .expect("Error with internal handling of non-unique type names")
57                    })
58            }),
59            subscription: inner.subscription().and_then(|subscription| {
60                cache
61                    .get_or_create_type_definition(TypeDefinitionReference::Object(subscription))
62                    .map(|td| {
63                        td.as_object()
64                            .expect("Error with internal handling of non-unique type names")
65                    })
66            }),
67            interface_implementors: FrozenMap::new(),
68            type_definitions_and_directive_definitions: OnceCell::new(),
69            directives: inner.directives().map(|d| Directives::new(d, cache)),
70        })
71    }
72
73    pub fn inner(&self) -> &'a S {
74        self.cache.inner_schema_definition()
75    }
76
77    pub fn cache(&self) -> &'a Cache<'a, S, W> {
78        self.cache
79    }
80
81    fn type_definitions_and_directive_definitions(
82        &self,
83    ) -> &TypeDefinitionsAndDirectiveDefinitions<'a, S, W> {
84        self.type_definitions_and_directive_definitions
85            .get_or_init(|| VisibilityVisitor::visit(self))
86    }
87
88    /// Almost identical type signature to `bluejay_core::definition::SchemaDefinition::get_directive_definition`
89    /// except for the lifetime on the return type.
90    fn get_directive_definition(&self, name: &str) -> Option<&'a DirectiveDefinition<'a, S, W>> {
91        self.cache.get_directive_definition(name).or_else(|| {
92            self.inner()
93                .get_directive_definition(name)
94                .and_then(|dd| self.cache.get_or_create_directive_definition(dd))
95        })
96    }
97
98    /// Almost identical type signature to `bluejay_core::definition::SchemaDefinition::get_interface_implementors`
99    /// except for the lifetime on the return type.
100    fn get_interface_implementors(
101        &self,
102        itd: &InterfaceTypeDefinition<'a, S, W>,
103    ) -> std::iter::Copied<std::slice::Iter<'_, &'a ObjectTypeDefinition<'a, S, W>>> {
104        self.interface_implementors
105            .get(itd.name())
106            .map(|ii| ii.iter())
107            .unwrap_or_else(|| {
108                let interface_implementors = self
109                    .inner()
110                    .get_interface_implementors(itd.inner())
111                    .filter_map(|otd| {
112                        let otd = self
113                            .cache
114                            .get_or_create_type_definition(
115                                definition::TypeDefinitionReference::Object(otd),
116                            )?
117                            .as_object()
118                            .unwrap();
119
120                        otd.interface_implementations()
121                            .is_some_and(|interface_implementations| {
122                                interface_implementations
123                                    .iter()
124                                    .any(|ii| ii.name() == itd.name())
125                            })
126                            .then_some(otd)
127                    })
128                    .collect();
129                self.interface_implementors
130                    .insert(itd.inner().name(), interface_implementors)
131                    .iter()
132            })
133            .copied()
134    }
135}
136
137impl<'a, S: definition::SchemaDefinition + 'a, W: Warden<SchemaDefinition = S>>
138    definition::SchemaDefinition for SchemaDefinition<'a, S, W>
139{
140    type Directive = Directive<'a, S, W>;
141    type Directives = Directives<'a, S, W>;
142    type InputValueDefinition = InputValueDefinition<'a, S, W>;
143    type InputFieldsDefinition = InputFieldsDefinition<'a, S, W>;
144    type ArgumentsDefinition = ArgumentsDefinition<'a, S, W>;
145    type EnumValueDefinition = EnumValueDefinition<'a, S, W>;
146    type EnumValueDefinitions = EnumValueDefinitions<'a, S, W>;
147    type FieldDefinition = FieldDefinition<'a, S, W>;
148    type FieldsDefinition = FieldsDefinition<'a, S, W>;
149    type InterfaceImplementation = InterfaceImplementation<'a, S, W>;
150    type InterfaceImplementations = InterfaceImplementations<'a, S, W>;
151    type UnionMemberType = UnionMemberType<'a, S, W>;
152    type UnionMemberTypes = UnionMemberTypes<'a, S, W>;
153    type InputType = InputType<'a, S, W>;
154    type OutputType = OutputType<'a, S, W>;
155    type CustomScalarTypeDefinition = ScalarTypeDefinition<'a, S, W>;
156    type ObjectTypeDefinition = ObjectTypeDefinition<'a, S, W>;
157    type InterfaceTypeDefinition = InterfaceTypeDefinition<'a, S, W>;
158    type UnionTypeDefinition = UnionTypeDefinition<'a, S, W>;
159    type InputObjectTypeDefinition = InputObjectTypeDefinition<'a, S, W>;
160    type EnumTypeDefinition = EnumTypeDefinition<'a, S, W>;
161    type TypeDefinition = TypeDefinition<'a, S, W>;
162    type DirectiveDefinition = DirectiveDefinition<'a, S, W>;
163    type TypeDefinitions<'b>
164        = std::iter::Copied<
165        btree_map::Values<'b, &'a str, TypeDefinitionReference<'b, Self::TypeDefinition>>,
166    >
167    where
168        'a: 'b;
169    type DirectiveDefinitions<'b>
170        = std::iter::Copied<btree_map::Values<'b, &'a str, &'b Self::DirectiveDefinition>>
171    where
172        'a: 'b;
173    type InterfaceImplementors<'b>
174        = std::iter::Copied<std::slice::Iter<'b, &'b Self::ObjectTypeDefinition>>
175    where
176        'a: 'b;
177
178    fn description(&self) -> Option<&str> {
179        self.inner().description()
180    }
181
182    fn query(&self) -> &Self::ObjectTypeDefinition {
183        self.query
184    }
185
186    fn mutation(&self) -> Option<&Self::ObjectTypeDefinition> {
187        self.mutation
188    }
189
190    fn subscription(&self) -> Option<&Self::ObjectTypeDefinition> {
191        self.subscription
192    }
193
194    fn type_definitions(&self) -> Self::TypeDefinitions<'_> {
195        self.type_definitions_and_directive_definitions()
196            .type_definitions
197            .values()
198            .copied()
199    }
200
201    fn get_type_definition(
202        &self,
203        name: &str,
204    ) -> Option<definition::TypeDefinitionReference<Self::TypeDefinition>> {
205        self.cache
206            .get_type_definition(name)
207            .or_else(|| {
208                self.cache
209                    .warden()
210                    .type_definitions_for_name(self.inner(), name)
211                    .find_map(|tdr| self.cache.get_or_create_type_definition(tdr))
212            })
213            .map(TypeDefinition::as_ref)
214    }
215
216    fn get_interface_implementors(
217        &self,
218        itd: &Self::InterfaceTypeDefinition,
219    ) -> Self::InterfaceImplementors<'_> {
220        SchemaDefinition::get_interface_implementors(self, itd)
221    }
222
223    fn directive_definitions(&self) -> Self::DirectiveDefinitions<'_> {
224        self.type_definitions_and_directive_definitions()
225            .directive_definitions
226            .values()
227            .copied()
228    }
229
230    fn get_directive_definition(&self, name: &str) -> Option<&Self::DirectiveDefinition> {
231        SchemaDefinition::get_directive_definition(self, name)
232    }
233}
234
235impl<'a, S: definition::SchemaDefinition + 'a, W: Warden<SchemaDefinition = S>> HasDirectives
236    for SchemaDefinition<'a, S, W>
237{
238    type Directives = Directives<'a, S, W>;
239
240    fn directives(&self) -> Option<&Self::Directives> {
241        self.directives.as_ref()
242    }
243}
244
245struct TypeDefinitionsAndDirectiveDefinitions<
246    'a,
247    S: definition::SchemaDefinition,
248    W: Warden<SchemaDefinition = S>,
249> {
250    type_definitions: BTreeMap<&'a str, TypeDefinitionReference<'a, TypeDefinition<'a, S, W>>>,
251    directive_definitions: BTreeMap<&'a str, &'a DirectiveDefinition<'a, S, W>>,
252}
253
254impl<'a, S: definition::SchemaDefinition, W: Warden<SchemaDefinition = S>>
255    From<VisibilityVisitor<'a, '_, S, W>> for TypeDefinitionsAndDirectiveDefinitions<'a, S, W>
256{
257    fn from(value: VisibilityVisitor<'a, '_, S, W>) -> Self {
258        let VisibilityVisitor {
259            type_definitions,
260            directive_definitions,
261            ..
262        } = value;
263        Self {
264            type_definitions,
265            directive_definitions,
266        }
267    }
268}
269
270struct VisibilityVisitor<'a, 'b, S: definition::SchemaDefinition, W: Warden<SchemaDefinition = S>> {
271    schema_definition: &'b SchemaDefinition<'a, S, W>,
272    type_definitions: BTreeMap<&'a str, TypeDefinitionReference<'a, TypeDefinition<'a, S, W>>>,
273    directive_definitions: BTreeMap<&'a str, &'a DirectiveDefinition<'a, S, W>>,
274}
275
276impl<'a, 'b, S: definition::SchemaDefinition, W: Warden<SchemaDefinition = S>>
277    VisibilityVisitor<'a, 'b, S, W>
278{
279    fn visit(
280        schema_definition: &'b SchemaDefinition<'a, S, W>,
281    ) -> TypeDefinitionsAndDirectiveDefinitions<'a, S, W> {
282        let mut instance = Self {
283            schema_definition,
284            type_definitions: BTreeMap::new(),
285            directive_definitions: BTreeMap::new(),
286        };
287
288        // all builtin directive definitions are needed for introspection regardless of whether or not they are accessible in the query
289        // also any directive definition that can be used in executable documents must be included here as they don't need to be accessible
290        // through the schema visit
291        schema_definition
292            .inner()
293            .directive_definitions()
294            .for_each(|dd| {
295                if dd.is_builtin() || dd.locations().iter().any(DirectiveLocation::is_executable) {
296                    if let Some(dd) = schema_definition
297                        .cache
298                        .get_or_create_directive_definition(dd)
299                    {
300                        instance.visit_directive_definition(dd);
301                    }
302                }
303            });
304
305        instance.visit_type_definition(TypeDefinitionReference::Object(schema_definition.query));
306
307        if let Some(mutation) = schema_definition.mutation {
308            instance.visit_type_definition(TypeDefinitionReference::Object(mutation));
309        }
310
311        if let Some(subscription) = schema_definition.subscription {
312            instance.visit_type_definition(TypeDefinitionReference::Object(subscription));
313        }
314
315        instance.into()
316    }
317
318    fn visit_type_definition(
319        &mut self,
320        type_definition: TypeDefinitionReference<'a, TypeDefinition<'a, S, W>>,
321    ) {
322        if let Entry::Vacant(entry) = self.type_definitions.entry(type_definition.name()) {
323            entry.insert(type_definition);
324
325            match type_definition {
326                TypeDefinitionReference::BuiltinScalar(_) => {}
327                TypeDefinitionReference::CustomScalar(cstd) => {
328                    self.visit_custom_scalar_type_definition(cstd)
329                }
330                TypeDefinitionReference::Object(otd) => self.visit_object_type_definition(otd),
331                TypeDefinitionReference::Interface(itd) => {
332                    self.visit_interface_type_definition(itd)
333                }
334                TypeDefinitionReference::Union(utd) => self.visit_union_type_definition(utd),
335                TypeDefinitionReference::Enum(etd) => self.visit_enum_type_definition(etd),
336                TypeDefinitionReference::InputObject(iotd) => {
337                    self.visit_input_object_type_definition(iotd)
338                }
339            }
340        }
341    }
342
343    fn visit_custom_scalar_type_definition(
344        &mut self,
345        custom_scalar_type_definition: &'a ScalarTypeDefinition<'a, S, W>,
346    ) {
347        self.visit_directives(custom_scalar_type_definition.directives());
348    }
349
350    fn visit_object_type_definition(
351        &mut self,
352        object_type_definition: &'a ObjectTypeDefinition<'a, S, W>,
353    ) {
354        self.visit_directives(object_type_definition.directives());
355        self.visit_fields_definition(object_type_definition.fields_definition());
356        if let Some(interface_implementations) = object_type_definition.interface_implementations()
357        {
358            self.visit_interface_implementations(interface_implementations);
359        }
360    }
361
362    fn visit_interface_type_definition(
363        &mut self,
364        interface_type_definition: &'a InterfaceTypeDefinition<'a, S, W>,
365    ) {
366        self.visit_directives(interface_type_definition.directives());
367        self.visit_fields_definition(interface_type_definition.fields_definition());
368        if let Some(interface_implementations) =
369            interface_type_definition.interface_implementations()
370        {
371            self.visit_interface_implementations(interface_implementations);
372        }
373        self.schema_definition
374            .get_interface_implementors(interface_type_definition)
375            .for_each(|otd| {
376                self.visit_type_definition(TypeDefinitionReference::Object(otd));
377            })
378    }
379
380    fn visit_union_type_definition(
381        &mut self,
382        union_type_definition: &'a UnionTypeDefinition<'a, S, W>,
383    ) {
384        self.visit_directives(union_type_definition.directives());
385        union_type_definition
386            .union_member_types()
387            .iter()
388            .for_each(|member_type| {
389                self.visit_type_definition(TypeDefinitionReference::Object(
390                    member_type.member_type(),
391                ))
392            })
393    }
394
395    fn visit_enum_type_definition(
396        &mut self,
397        enum_type_definition: &'a EnumTypeDefinition<'a, S, W>,
398    ) {
399        self.visit_directives(enum_type_definition.directives());
400        enum_type_definition
401            .enum_value_definitions()
402            .iter()
403            .for_each(|etd| {
404                self.visit_directives(etd.directives());
405            })
406    }
407
408    fn visit_input_object_type_definition(
409        &mut self,
410        input_object_type_definition: &'a InputObjectTypeDefinition<'a, S, W>,
411    ) {
412        self.visit_directives(input_object_type_definition.directives());
413        input_object_type_definition
414            .input_field_definitions()
415            .iter()
416            .for_each(|ivd| {
417                self.visit_input_value_definition(ivd);
418            })
419    }
420
421    fn visit_directives(&mut self, directives: Option<&'a Directives<'a, S, W>>) {
422        if let Some(directives) = directives {
423            directives.iter().for_each(|directive| {
424                // if this is `None` it means that the schema is invalid, but that is not the responsibility of this visitor
425                // and should be added to `bluejay-validator`
426                if let Some(directive_definition) = self
427                    .schema_definition
428                    .get_directive_definition(directive.name())
429                {
430                    self.visit_directive_definition(directive_definition);
431                }
432            })
433        }
434    }
435
436    fn visit_directive_definition(
437        &mut self,
438        directive_definition: &'a DirectiveDefinition<'a, S, W>,
439    ) {
440        if let Entry::Vacant(entry) = self
441            .directive_definitions
442            .entry(directive_definition.name())
443        {
444            entry.insert(directive_definition);
445            if let Some(arguments_definition) = directive_definition.arguments_definition() {
446                self.visit_arguments_definition(arguments_definition);
447            }
448        }
449    }
450
451    fn visit_fields_definition(&mut self, fields_definition: &'a FieldsDefinition<'a, S, W>) {
452        fields_definition.iter().for_each(|field| {
453            self.visit_field_definition(field);
454        })
455    }
456
457    fn visit_field_definition(&mut self, field_definition: &'a FieldDefinition<'a, S, W>) {
458        self.visit_directives(field_definition.directives());
459        if let Some(arguments_definition) = field_definition.arguments_definition() {
460            self.visit_arguments_definition(arguments_definition);
461        }
462        let base_type = field_definition.r#type().base();
463        self.visit_type_definition(base_type.into())
464    }
465
466    fn visit_arguments_definition(
467        &mut self,
468        arguments_definition: &'a ArgumentsDefinition<'a, S, W>,
469    ) {
470        arguments_definition.iter().for_each(|ivd| {
471            self.visit_input_value_definition(ivd);
472        })
473    }
474
475    fn visit_input_value_definition(
476        &mut self,
477        input_value_definition: &'a InputValueDefinition<'a, S, W>,
478    ) {
479        self.visit_directives(input_value_definition.directives());
480        let base_type = input_value_definition.r#type().base();
481        self.visit_type_definition(base_type.into())
482    }
483
484    fn visit_interface_implementations(
485        &mut self,
486        interface_implementations: &'a InterfaceImplementations<'a, S, W>,
487    ) {
488        interface_implementations.iter().for_each(|ii| {
489            self.visit_type_definition(TypeDefinitionReference::Interface(ii.interface()));
490        })
491    }
492}