use bumpalo::collections::Vec;
use super::super::{ValidationContext, ValidationRule};
use crate::{ast::*, visit::*};
pub struct UniqueOperationNames<'a> {
used_operation_names: Vec<'a, &'a str>,
}
impl<'a> DefaultIn<'a> for UniqueOperationNames<'a> {
fn default_in(arena: &'a bumpalo::Bump) -> Self {
Self {
used_operation_names: Vec::new_in(arena),
}
}
}
impl<'a> ValidationRule<'a> for UniqueOperationNames<'a> {}
impl<'a> Visitor<'a, ValidationContext<'a>> for UniqueOperationNames<'a> {
fn enter_operation(
&mut self,
ctx: &mut ValidationContext<'a>,
operation: &'a OperationDefinition<'a>,
_info: &VisitInfo,
) -> VisitFlow {
if let Some(name) = operation.name {
if self.used_operation_names.contains(&name.name) {
ctx.add_error("All defined operations must have unique names");
VisitFlow::Break
} else {
self.used_operation_names.push(name.name);
VisitFlow::Skip
}
} else {
VisitFlow::Skip
}
}
fn enter_fragment(
&mut self,
_ctx: &mut ValidationContext<'a>,
_fragment: &'a FragmentDefinition,
_info: &VisitInfo,
) -> VisitFlow {
VisitFlow::Skip
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn valid_operation_names() {
let ctx = ASTContext::new();
let document = Document::parse(&ctx, "query Root { __typename }").unwrap();
UniqueOperationNames::validate(&ctx, document).unwrap();
}
#[test]
fn overlapping_fragment_names() {
let ctx = ASTContext::new();
let document = Document::parse(
&ctx,
"query Root { __typename } mutation Root { __typename }",
)
.unwrap();
UniqueOperationNames::validate(&ctx, document).unwrap_err();
}
}