graphql-schema-validation 0.1.3

A spec compliant implementation of GraphQL SDL schema validation
Documentation
use super::*;

pub(crate) fn validate_directive_definition<'a>(
    definition: &'a Positioned<ast::DirectiveDefinition>,
    ctx: &mut Context<'a>,
) {
    let directive_name = definition.node.name.node.as_str();
    if definition.node.name.node.starts_with("__") {
        ctx.push_error(miette::miette! {
            r#"Directive names must not start with "__""#,
        });
    }

    for arg in &definition.node.arguments {
        let type_name = extract_type_name(&arg.node.ty.node.base);
        let location = || format!("@{directive_name}({}:)", arg.node.name.node);
        match validate_input_type(type_name, arg.node.ty.pos, ctx) {
            ValidateInputTypeResult::Ok => (),
            ValidateInputTypeResult::UnknownType => diagnostics::unknown_type(type_name, &location(), ctx),
            ValidateInputTypeResult::NotAnInputType => {
                diagnostics::output_type_in_input_position(type_name, &location(), ctx);
            }
        }
    }

    ctx.directive_names
        .insert(definition.node.name.node.as_str(), definition);
}

pub(crate) fn validate_directives<'a>(
    directives: &'a [Positioned<ast::ConstDirective>],
    location: ast::DirectiveLocation,
    ctx: &mut Context<'a>,
) {
    let names = directives.iter().map(|d| d.node.name.node.as_str());
    ctx.find_duplicates(names, |ctx, first, _| {
        let directive_name = directives[first].node.name.node.as_str();
        if ctx
            .directive_names
            .get(directive_name)
            .map(|directive| directive.node.is_repeatable)
            .unwrap_or(true)
        {
            return;
        }

        ctx.push_error(miette::miette!("{directive_name} is not repeatable"));
    });

    for directive in directives {
        let directive_name = directive.node.name.node.as_str();
        if let Some(definition) = ctx.directive_names.get(directive_name) {
            if !definition.node.locations.iter().any(|loc| loc.node == location) {
                ctx.push_error(miette::miette!(
                    "Directive @{directive_name} used at an invalid location: {:?}",
                    location
                ));
            }
        }
    }
}