async-graphql/src/validation/rules/variables_in_allowed_position.rs

92 lines
3.0 KiB
Rust
Raw Normal View History

2020-03-10 06:14:09 +00:00
use crate::registry::TypeName;
2020-03-24 10:54:22 +00:00
use crate::validation::visitor::{Visitor, VisitorContext};
2020-03-10 06:14:09 +00:00
use crate::Value;
use graphql_parser::query::{Field, OperationDefinition, VariableDefinition};
use graphql_parser::schema::Directive;
use graphql_parser::Pos;
use std::collections::HashMap;
#[derive(Default)]
pub struct VariableInAllowedPosition<'a> {
var_types: HashMap<&'a str, String>,
}
impl<'a> VariableInAllowedPosition<'a> {
fn check_type(
&mut self,
2020-03-22 08:45:59 +00:00
ctx: &mut VisitorContext<'a>,
2020-03-10 06:14:09 +00:00
pos: Pos,
except_type: &str,
value: &Value,
) {
let ty = TypeName::create(except_type);
match (ty, value) {
(_, Value::Variable(name)) => {
if let Some(var_type) = self.var_types.get(name.as_str()) {
if except_type != var_type {
ctx.report_error(
vec![pos],
format!(
"Variable \"{}\" of type \"{}\" used in position expecting type \"{}\"",
name, var_type, except_type
),
);
}
}
}
(TypeName::List(elem_type), Value::List(values)) => {
for value in values {
self.check_type(ctx, pos, elem_type, value);
}
}
(TypeName::NonNull(elem_type), value) => {
self.check_type(ctx, pos, elem_type, value);
}
_ => {}
}
}
}
impl<'a> Visitor<'a> for VariableInAllowedPosition<'a> {
fn enter_operation_definition(
&mut self,
2020-03-22 08:45:59 +00:00
_ctx: &mut VisitorContext<'a>,
2020-03-10 06:14:09 +00:00
_operation_definition: &'a OperationDefinition,
) {
self.var_types.clear();
}
fn enter_variable_definition(
&mut self,
2020-03-22 08:45:59 +00:00
_ctx: &mut VisitorContext<'a>,
2020-03-10 06:14:09 +00:00
variable_definition: &'a VariableDefinition,
) {
self.var_types.insert(
variable_definition.name.as_str(),
variable_definition.var_type.to_string(),
);
}
2020-03-22 08:45:59 +00:00
fn enter_directive(&mut self, ctx: &mut VisitorContext<'a>, directive: &'a Directive) {
2020-03-10 06:14:09 +00:00
if let Some(schema_directive) = ctx.registry.directives.get(directive.name.as_str()) {
for (name, value) in &directive.arguments {
if let Some(input) = schema_directive.args.get(name.as_str()) {
self.check_type(ctx, directive.position, &input.ty, value);
}
}
}
}
2020-03-22 08:45:59 +00:00
fn enter_field(&mut self, ctx: &mut VisitorContext<'a>, field: &'a Field) {
2020-03-10 06:14:09 +00:00
if let Some(parent_type) = ctx.parent_type() {
if let Some(schema_field) = parent_type.field_by_name(&field.name) {
for (name, value) in &field.arguments {
if let Some(arg) = schema_field.args.get(name.as_str()) {
self.check_type(ctx, field.position, &arg.ty, value);
}
}
}
}
}
}