juniper-from-schema-code-gen 0.5.2

Internal code generation crate for juniper-from-schema
Documentation
use crate::ast_pass::{
    directive_parsing::{DateTimeScalarType, ParseDirective},
    error::{Error, ErrorKind},
    schema_visitor::SchemaVisitor,
    type_name, EmitError,
};
use graphql_parser::{
    schema::{Document, *},
    Pos,
};
use std::collections::{BTreeSet, HashMap, HashSet};

#[derive(Debug)]
pub struct AstData<'doc> {
    interface_implementors: HashMap<&'doc str, Vec<&'doc str>>,
    user_scalars: HashSet<&'doc str>,
    enum_variants: HashSet<&'doc str>,
    input_object_field_types: HashMap<&'doc str, HashMap<&'doc String, &'doc Type>>,
    errors: BTreeSet<Error<'doc>>,
    raw_schema: &'doc str,
    include_time_zone_on_date_time_scalar: bool,
}

impl<'doc> SchemaVisitor<'doc> for AstData<'doc> {
    fn visit_object_type(&mut self, obj: &'doc ObjectType) {
        for interface in &obj.implements_interfaces {
            self.interface_implementors
                .entry(interface)
                .or_insert_with(Vec::new)
                .push(&obj.name);
        }
    }

    fn visit_scalar_type(&mut self, scalar: &'doc ScalarType) {
        match &*scalar.name {
            name if name == crate::DATE_TIME_SCALAR_NAME => {
                let args = self.parse_directives(DateTimeScalarType(scalar));
                if args.with_time_zone {
                    self.include_time_zone_on_date_time_scalar = true;
                } else {
                    self.include_time_zone_on_date_time_scalar = false;
                }
                self.user_scalars.insert(name);
            }
            name => {
                self.user_scalars.insert(name);
            }
        };
    }

    fn visit_enum_type(&mut self, enum_type: &'doc EnumType) {
        self.enum_variants.insert(&enum_type.name);
    }

    fn visit_input_object_type(&mut self, input_type: &'doc InputObjectType) {
        for field in &input_type.fields {
            self.input_object_field_types
                .entry(&input_type.name)
                .or_insert_with(HashMap::new)
                .insert(&field.name, &field.value_type);
        }
    }
}

impl<'doc> AstData<'doc> {
    pub fn new_from_schema_and_doc(
        raw_schema: &'doc str,
        doc: &'doc Document,
    ) -> Result<Self, BTreeSet<Error<'doc>>> {
        let mut data = Self::new(raw_schema);
        data.visit_document(doc);

        if data.errors.is_empty() {
            Ok(data)
        } else {
            Err(data.errors)
        }
    }

    fn new(raw_schema: &'doc str) -> Self {
        Self {
            interface_implementors: Default::default(),
            user_scalars: Default::default(),
            enum_variants: Default::default(),
            input_object_field_types: Default::default(),
            errors: Default::default(),
            raw_schema,
            include_time_zone_on_date_time_scalar: true,
        }
    }

    pub fn get_implementors_of_interface(&self, name: &str) -> Option<&Vec<&str>> {
        self.interface_implementors.get(name)
    }

    pub fn date_scalar_defined(&self) -> bool {
        self.is_scalar(crate::DATE_SCALAR_NAME)
    }

    pub fn date_time_scalar_defined(&self) -> bool {
        self.is_scalar(crate::DATE_TIME_SCALAR_NAME)
    }

    pub fn date_time_scalar_definition(&self) -> Option<DateTimeScalarDefinition> {
        if self.is_scalar(crate::DATE_TIME_SCALAR_NAME) {
            if self.include_time_zone_on_date_time_scalar {
                Some(DateTimeScalarDefinition::WithTimeZone)
            } else {
                Some(DateTimeScalarDefinition::WithoutTimeZone)
            }
        } else {
            None
        }
    }

    pub fn uuid_scalar_defined(&self) -> bool {
        self.is_scalar(crate::UUID_SCALAR_NAME)
    }

    pub fn url_scalar_defined(&self) -> bool {
        self.is_scalar(crate::URL_SCALAR_NAME)
    }

    pub fn is_scalar(&self, name: &str) -> bool {
        self.user_scalars.contains(name)
    }

    pub fn is_enum_variant(&self, name: &str) -> bool {
        self.enum_variants.contains(name)
    }

    #[allow(clippy::ptr_arg)]
    pub fn input_object_field_is_nullable(
        &self,
        input_type_name: &'doc str,
        field_name: &'doc String,
    ) -> Option<bool> {
        use graphql_parser::query::Type::*;

        let field_map = self.input_object_field_types.get(input_type_name)?;
        let type_ = field_map.get(field_name)?;
        match type_ {
            NamedType(_) => Some(true),
            ListType(_) => Some(true),
            NonNullType(_) => Some(false),
        }
    }

    pub fn input_object_field_names(
        &self,
        input_type_name: &'doc str,
    ) -> Option<HashSet<&'doc &String>> {
        let field_map = self.input_object_field_types.get(input_type_name)?;
        let mut out = HashSet::new();
        for key in field_map.keys() {
            out.insert(key);
        }
        Some(out)
    }

    #[allow(clippy::ptr_arg)]
    pub fn input_object_field_type_name(
        &self,
        input_type_name: &'doc str,
        field_name: &'doc String,
    ) -> Option<&'doc Name> {
        let field_map = &self.input_object_field_types.get(input_type_name)?;
        let type_ = field_map.get(field_name)?;
        Some(type_name(&type_))
    }
}

impl<'doc> EmitError<'doc> for AstData<'doc> {
    fn emit_non_fatal_error(&mut self, pos: Pos, kind: ErrorKind<'doc>) {
        let error = Error {
            pos,
            kind,
            raw_schema: &self.raw_schema,
        };
        self.errors.insert(error);
    }
}

pub enum DateTimeScalarDefinition {
    WithTimeZone,
    WithoutTimeZone,
}