bluejay-validator 0.3.1

A GraphQL validator
Documentation
use crate::definition::{Error, Rule, Visitor};
use bluejay_core::definition::{
    BaseInputTypeReference, InputObjectTypeDefinition, InputType, InputTypeReference,
    InputValueDefinition, SchemaDefinition,
};
use bluejay_core::AsIter;
use std::collections::HashSet;

pub struct InputObjectCircularReferences<'a, S: SchemaDefinition + 'a> {
    schema_definition: &'a S,
    errors: Vec<Error<'a, S>>,
}

impl<'a, S: SchemaDefinition> Visitor<'a, S> for InputObjectCircularReferences<'a, S> {
    fn visit_input_object_type_definition(
        &mut self,
        input_object_type_definition: &'a <S as SchemaDefinition>::InputObjectTypeDefinition,
    ) {
        let mut circular_references = Vec::new();
        Self::visit_for_circular_references(
            self.schema_definition,
            input_object_type_definition,
            input_object_type_definition,
            &mut circular_references,
            &mut HashSet::new(),
        );

        if !circular_references.is_empty() {
            self.errors
                .push(Error::InputObjectTypeDefinitionCircularReferences {
                    input_object_type_definition,
                    circular_references,
                });
        }
    }
}

impl<'a, S: SchemaDefinition + 'a> InputObjectCircularReferences<'a, S> {
    fn visit_for_circular_references(
        schema_definition: &'a S,
        target: &'a S::InputObjectTypeDefinition,
        iotd: &'a S::InputObjectTypeDefinition,
        circular_references: &mut Vec<&'a S::InputType>,
        encountered: &mut HashSet<&'a str>,
    ) {
        iotd.input_field_definitions().iter().for_each(|ivd| {
            match ivd.r#type().as_ref(schema_definition) {
                InputTypeReference::Base(inner, required) if required => {
                    if inner.name() == target.name() {
                        circular_references.push(ivd.r#type());
                    } else if let BaseInputTypeReference::InputObject(inner_iotd) = inner {
                        if encountered.insert(inner_iotd.name()) {
                            Self::visit_for_circular_references(
                                schema_definition,
                                target,
                                inner_iotd,
                                circular_references,
                                encountered,
                            );
                        }
                    }
                }
                _ => {}
            }
        });
    }
}

impl<'a, S: SchemaDefinition> IntoIterator for InputObjectCircularReferences<'a, S> {
    type Item = Error<'a, S>;
    type IntoIter = std::vec::IntoIter<Error<'a, S>>;

    fn into_iter(self) -> Self::IntoIter {
        self.errors.into_iter()
    }
}

impl<'a, S: SchemaDefinition> Rule<'a, S> for InputObjectCircularReferences<'a, S> {
    type Error = Error<'a, S>;

    fn new(schema_definition: &'a S) -> Self {
        Self {
            schema_definition,
            errors: Vec::new(),
        }
    }
}