use std::fmt::Debug;
use std::fmt::Display;
use std::fmt::Formatter;
use std::ops::Deref;
use apollo_compiler::Name;
use apollo_compiler::Node;
use apollo_compiler::Schema;
use apollo_compiler::ast;
use apollo_compiler::ast::Argument;
use apollo_compiler::ast::DirectiveLocation;
use apollo_compiler::name;
use apollo_compiler::schema::Component;
use apollo_compiler::schema::ComponentName;
use apollo_compiler::schema::ComponentOrigin;
use apollo_compiler::schema::Directive;
use apollo_compiler::schema::DirectiveDefinition;
use apollo_compiler::schema::EnumType;
use apollo_compiler::schema::EnumValueDefinition;
use apollo_compiler::schema::ExtendedType;
use apollo_compiler::schema::FieldDefinition;
use apollo_compiler::schema::InputObjectType;
use apollo_compiler::schema::InputValueDefinition;
use apollo_compiler::schema::InterfaceType;
use apollo_compiler::schema::ObjectType;
use apollo_compiler::schema::ScalarType;
use apollo_compiler::schema::SchemaDefinition;
use apollo_compiler::schema::UnionType;
use either::Either;
use serde::Serialize;
use strum::IntoEnumIterator;
use crate::bail;
use crate::error::CompositionError;
use crate::error::FederationError;
use crate::error::SingleFederationError;
use crate::link::database::links_metadata;
use crate::link::spec_definition::SpecDefinition;
use crate::merger::merge_argument::HasArguments;
use crate::merger::merge_argument::HasDefaultValue;
use crate::merger::merge_enum::EnumExampleAst;
use crate::schema::FederationSchema;
use crate::schema::referencer::DirectiveReferencers;
use crate::schema::referencer::EnumTypeReferencers;
use crate::schema::referencer::InputObjectTypeReferencers;
use crate::schema::referencer::InterfaceTypeReferencers;
use crate::schema::referencer::ObjectTypeReferencers;
use crate::schema::referencer::Referencers;
use crate::schema::referencer::ScalarTypeReferencers;
use crate::schema::referencer::UnionTypeReferencers;
#[derive(Debug, thiserror::Error)]
pub(crate) enum PositionLookupError {
#[error("Schema has no directive `{0}`")]
DirectiveMissing(DirectiveDefinitionPosition),
#[error("Schema has no type `{0}`")]
TypeMissing(Name),
#[error("Schema type `{0}` is not {1}")]
TypeWrongKind(Name, &'static str),
#[error("{0} type `{1}` has no field `{2}`")]
MissingField(&'static str, Name, Name),
#[error("Directive `{}` has no argument `{}`", .0.directive_name, .0.argument_name)]
MissingDirectiveArgument(DirectiveArgumentDefinitionPosition),
#[error("{0} `{1}.{2}` has no argument `{3}`")]
MissingFieldArgument(&'static str, Name, Name, Name),
#[error("Enum type `{}` has no value `{}`", .0.type_name, .0.value_name)]
MissingValue(EnumValueDefinitionPosition),
#[error("Cannot mutate reserved {0} `{1}.{2}`")]
MutateReservedField(&'static str, Name, Name),
}
impl From<PositionLookupError> for FederationError {
fn from(value: PositionLookupError) -> Self {
FederationError::internal(value.to_string())
}
}
#[derive(Debug, thiserror::Error)]
#[error("Type `{actual}` was unexpectedly not {expected}")]
pub(crate) struct PositionConvertError<T: Debug + Display> {
actual: T,
expected: &'static str,
}
impl<T: Debug + Display> From<PositionConvertError<T>> for FederationError {
fn from(value: PositionConvertError<T>) -> Self {
FederationError::internal(value.to_string())
}
}
impl<T: Debug + Display> From<PositionConvertError<T>> for CompositionError {
fn from(value: PositionConvertError<T>) -> Self {
CompositionError::InternalError {
message: value.to_string(),
}
}
}
macro_rules! fallible_conversions {
( $from:ident :: $branch:ident -> $to:ident ) => {
impl TryFrom<$from> for $to {
type Error = PositionConvertError<$from>;
fn try_from(value: $from) -> Result<Self, Self::Error> {
match value {
$from::$branch(value) => Ok(value),
_ => Err(PositionConvertError {
actual: value,
expected: $to::EXPECTED,
}),
}
}
}
};
( $from:ident :: { $($branch:ident),+ } -> $to:ident ) => {
impl TryFrom<$from> for $to {
type Error = PositionConvertError<$from>;
fn try_from(value: $from) -> Result<Self, Self::Error> {
match value {
$(
$from::$branch(value) => Ok($to::$branch(value)),
)+
_ => Err(PositionConvertError {
actual: value,
expected: $to::EXPECTED,
}),
}
}
}
}
}
macro_rules! infallible_conversions {
( $from:ident :: { $($branch:ident),+ } -> $to:ident ) => {
impl From<$from> for $to {
fn from(value: $from) -> Self {
match value {
$(
$from::$branch(value) => $to::$branch(value)
),+
}
}
}
}
}
pub(crate) trait HasDescription {
fn description<'schema>(&self, schema: &'schema FederationSchema)
-> Option<&'schema Node<str>>;
fn set_description(
&self,
schema: &mut FederationSchema,
description: Option<Node<str>>,
) -> Result<(), FederationError>;
fn is_schema_definition() -> bool;
}
macro_rules! impl_has_description_for {
($struct_name:ident) => {
impl HasDescription for $struct_name {
fn description<'schema>(
&self,
schema: &'schema FederationSchema,
) -> Option<&'schema Node<str>> {
self.try_get(&schema.schema)?.description.as_ref()
}
fn set_description(
&self,
schema: &mut FederationSchema,
description: Option<Node<str>>,
) -> Result<(), FederationError> {
self.make_mut(&mut schema.schema)?.make_mut().description = description;
Ok(())
}
fn is_schema_definition() -> bool {
false
}
}
};
}
impl_has_description_for!(DirectiveDefinitionPosition);
impl_has_description_for!(ScalarTypeDefinitionPosition);
impl_has_description_for!(ObjectTypeDefinitionPosition);
impl_has_description_for!(InterfaceTypeDefinitionPosition);
impl_has_description_for!(UnionTypeDefinitionPosition);
impl_has_description_for!(EnumTypeDefinitionPosition);
impl_has_description_for!(InputObjectTypeDefinitionPosition);
impl_has_description_for!(ObjectFieldDefinitionPosition);
impl_has_description_for!(InterfaceFieldDefinitionPosition);
impl_has_description_for!(EnumValueDefinitionPosition);
impl_has_description_for!(InputObjectFieldDefinitionPosition);
impl_has_description_for!(ObjectFieldArgumentDefinitionPosition);
impl_has_description_for!(InterfaceFieldArgumentDefinitionPosition);
impl_has_description_for!(DirectiveArgumentDefinitionPosition);
impl HasDescription for SchemaDefinitionPosition {
fn description<'schema>(
&self,
schema: &'schema FederationSchema,
) -> Option<&'schema Node<str>> {
self.get(&schema.schema).description.as_ref()
}
fn set_description(
&self,
schema: &mut FederationSchema,
description: Option<Node<str>>,
) -> Result<(), FederationError> {
self.make_mut(&mut schema.schema).make_mut().description = description;
Ok(())
}
fn is_schema_definition() -> bool {
true
}
}
impl HasDescription for ObjectOrInterfaceFieldDefinitionPosition {
fn description<'schema>(
&self,
schema: &'schema FederationSchema,
) -> Option<&'schema Node<str>> {
match self {
Self::Object(field) => field.description(schema),
Self::Interface(field) => field.description(schema),
}
}
fn set_description(
&self,
schema: &mut FederationSchema,
description: Option<Node<str>>,
) -> Result<(), FederationError> {
match self {
Self::Object(field) => field.set_description(schema, description),
Self::Interface(field) => field.set_description(schema, description),
}
}
fn is_schema_definition() -> bool {
false
}
}
impl HasDescription for FieldDefinitionPosition {
fn description<'schema>(
&self,
schema: &'schema FederationSchema,
) -> Option<&'schema Node<str>> {
match self {
FieldDefinitionPosition::Object(field) => field.description(schema),
FieldDefinitionPosition::Interface(field) => field.description(schema),
FieldDefinitionPosition::Union(field) => field.description(schema),
}
}
fn set_description(
&self,
schema: &mut FederationSchema,
description: Option<Node<str>>,
) -> Result<(), FederationError> {
match self {
FieldDefinitionPosition::Object(field) => field.set_description(schema, description),
FieldDefinitionPosition::Interface(field) => field.set_description(schema, description),
FieldDefinitionPosition::Union(field) => field.set_description(schema, description),
}
}
fn is_schema_definition() -> bool {
false
}
}
impl HasDescription for UnionTypenameFieldDefinitionPosition {
fn description<'schema>(
&self,
schema: &'schema FederationSchema,
) -> Option<&'schema Node<str>> {
self.get(&schema.schema)
.map_or(None, |field| field.description.as_ref())
}
fn set_description(
&self,
_schema: &mut FederationSchema,
_description: Option<Node<str>>,
) -> Result<(), FederationError> {
bail!("Description is immutable for union typename fields")
}
fn is_schema_definition() -> bool {
false
}
}
impl HasDescription for DirectiveTargetPosition {
fn description<'schema>(
&self,
schema: &'schema FederationSchema,
) -> Option<&'schema Node<str>> {
match self {
Self::Schema(pos) => pos.description(schema),
Self::ScalarType(pos) => pos.description(schema),
Self::ObjectType(pos) => pos.description(schema),
Self::ObjectField(pos) => pos.description(schema),
Self::InterfaceType(pos) => pos.description(schema),
Self::InterfaceField(pos) => pos.description(schema),
Self::UnionType(pos) => pos.description(schema),
Self::EnumType(pos) => pos.description(schema),
Self::EnumValue(pos) => pos.description(schema),
Self::InputObjectType(pos) => pos.description(schema),
_ => None,
}
}
fn set_description(
&self,
schema: &mut FederationSchema,
description: Option<Node<str>>,
) -> Result<(), FederationError> {
match self {
Self::Schema(pos) => pos.set_description(schema, description),
Self::ScalarType(pos) => pos.set_description(schema, description),
Self::ObjectType(pos) => pos.set_description(schema, description),
Self::ObjectField(pos) => pos.set_description(schema, description),
Self::InterfaceType(pos) => pos.set_description(schema, description),
Self::InterfaceField(pos) => pos.set_description(schema, description),
Self::UnionType(pos) => pos.set_description(schema, description),
Self::EnumType(pos) => pos.set_description(schema, description),
Self::EnumValue(pos) => pos.set_description(schema, description),
Self::InputObjectType(pos) => pos.set_description(schema, description),
_ => Err(FederationError::SingleFederationError(
SingleFederationError::Internal {
message: String::from(
"No valid conversion from DirectiveTargetPosition to desired type.",
),
},
)),
}
}
fn is_schema_definition() -> bool {
false
}
}
impl HasDescription for FieldArgumentDefinitionPosition {
fn description<'schema>(
&self,
schema: &'schema FederationSchema,
) -> Option<&'schema Node<str>> {
match self {
Self::Object(field) => field.description(schema),
Self::Interface(field) => field.description(schema),
}
}
fn set_description(
&self,
schema: &mut FederationSchema,
description: Option<Node<str>>,
) -> Result<(), FederationError> {
match self {
Self::Object(field) => field.set_description(schema, description),
Self::Interface(field) => field.set_description(schema, description),
}
}
fn is_schema_definition() -> bool {
false
}
}
pub(crate) trait HasType {
fn get_type<'schema>(
&self,
schema: &'schema FederationSchema,
) -> Result<&'schema ast::Type, FederationError>;
fn set_type(&self, schema: &mut FederationSchema, ty: ast::Type)
-> Result<(), FederationError>;
fn enum_example_ast(
&self,
schema: &FederationSchema,
) -> Result<EnumExampleAst, FederationError>;
fn is_argument() -> bool;
}
impl HasType for DirectiveArgumentDefinitionPosition {
fn get_type<'schema>(
&self,
schema: &'schema FederationSchema,
) -> Result<&'schema ast::Type, FederationError> {
Ok(&self.get(&schema.schema)?.ty)
}
fn set_type(
&self,
schema: &mut FederationSchema,
ty: ast::Type,
) -> Result<(), FederationError> {
*self.make_mut(&mut schema.schema)?.make_mut().ty.make_mut() = ty;
Ok(())
}
fn enum_example_ast(
&self,
schema: &FederationSchema,
) -> Result<EnumExampleAst, FederationError> {
let node = self.get(schema.schema())?.clone();
Ok(EnumExampleAst::Input(node))
}
fn is_argument() -> bool {
true
}
}
impl HasType for FieldArgumentDefinitionPosition {
fn get_type<'schema>(
&self,
schema: &'schema FederationSchema,
) -> Result<&'schema ast::Type, FederationError> {
Ok(self.get(&schema.schema)?.ty.as_ref())
}
fn set_type(
&self,
schema: &mut FederationSchema,
ty: ast::Type,
) -> Result<(), FederationError> {
*self.make_mut(&mut schema.schema)?.make_mut().ty.make_mut() = ty;
Ok(())
}
fn enum_example_ast(
&self,
schema: &FederationSchema,
) -> Result<EnumExampleAst, FederationError> {
let node = self.get(schema.schema())?.clone();
Ok(EnumExampleAst::Input(node))
}
fn is_argument() -> bool {
true
}
}
impl HasType for InputObjectFieldDefinitionPosition {
fn get_type<'schema>(
&self,
schema: &'schema FederationSchema,
) -> Result<&'schema ast::Type, FederationError> {
Ok(&self.get(&schema.schema)?.ty)
}
fn set_type(
&self,
schema: &mut FederationSchema,
ty: ast::Type,
) -> Result<(), FederationError> {
*self.make_mut(&mut schema.schema)?.make_mut().ty.make_mut() = ty;
Ok(())
}
fn enum_example_ast(
&self,
schema: &FederationSchema,
) -> Result<EnumExampleAst, FederationError> {
Ok(EnumExampleAst::Input(
self.get(schema.schema())?.clone().node,
))
}
fn is_argument() -> bool {
false
}
}
impl HasType for ObjectFieldDefinitionPosition {
fn get_type<'schema>(
&self,
schema: &'schema FederationSchema,
) -> Result<&'schema ast::Type, FederationError> {
Ok(&self.get(&schema.schema)?.ty)
}
fn set_type(
&self,
schema: &mut FederationSchema,
ty: ast::Type,
) -> Result<(), FederationError> {
self.make_mut(&mut schema.schema)?.make_mut().ty = ty;
Ok(())
}
fn enum_example_ast(
&self,
schema: &FederationSchema,
) -> Result<EnumExampleAst, FederationError> {
let node = self.get(schema.schema())?.clone().node;
Ok(EnumExampleAst::Field(node))
}
fn is_argument() -> bool {
false
}
}
impl HasType for InterfaceFieldDefinitionPosition {
fn get_type<'schema>(
&self,
schema: &'schema FederationSchema,
) -> Result<&'schema ast::Type, FederationError> {
Ok(&self.get(&schema.schema)?.ty)
}
fn set_type(
&self,
schema: &mut FederationSchema,
ty: ast::Type,
) -> Result<(), FederationError> {
self.make_mut(&mut schema.schema)?.make_mut().ty = ty;
Ok(())
}
fn enum_example_ast(
&self,
schema: &FederationSchema,
) -> Result<EnumExampleAst, FederationError> {
let node = self.get(schema.schema())?.clone().node;
Ok(EnumExampleAst::Field(node))
}
fn is_argument() -> bool {
false
}
}
impl HasType for ObjectOrInterfaceFieldDefinitionPosition {
fn get_type<'schema>(
&self,
schema: &'schema FederationSchema,
) -> Result<&'schema ast::Type, FederationError> {
match self {
Self::Object(field) => field.get_type(schema),
Self::Interface(field) => field.get_type(schema),
}
}
fn set_type(
&self,
schema: &mut FederationSchema,
ty: ast::Type,
) -> Result<(), FederationError> {
match self {
Self::Object(field) => field.set_type(schema, ty),
Self::Interface(field) => field.set_type(schema, ty),
}
}
fn enum_example_ast(
&self,
schema: &FederationSchema,
) -> Result<EnumExampleAst, FederationError> {
match self {
Self::Object(field) => field.enum_example_ast(schema),
Self::Interface(field) => field.enum_example_ast(schema),
}
}
fn is_argument() -> bool {
false
}
}
pub(crate) trait HasMutableDirectives {
fn directives_mut<'schema>(
&self,
schema: &'schema mut Schema,
) -> Result<&'schema mut ast::DirectiveList, FederationError>;
}
impl HasMutableDirectives for ObjectFieldDefinitionPosition {
fn directives_mut<'schema>(
&self,
schema: &'schema mut Schema,
) -> Result<&'schema mut ast::DirectiveList, FederationError> {
let field = self.make_mut(schema)?;
Ok(&mut field.make_mut().directives)
}
}
impl HasMutableDirectives for InterfaceFieldDefinitionPosition {
fn directives_mut<'schema>(
&self,
schema: &'schema mut Schema,
) -> Result<&'schema mut ast::DirectiveList, FederationError> {
let field = self.make_mut(schema)?;
Ok(&mut field.make_mut().directives)
}
}
impl HasMutableDirectives for InputObjectFieldDefinitionPosition {
fn directives_mut<'schema>(
&self,
schema: &'schema mut Schema,
) -> Result<&'schema mut ast::DirectiveList, FederationError> {
let field = self.make_mut(schema)?;
Ok(&mut field.make_mut().directives)
}
}
pub(crate) trait HasAppliedDirectives {
type AppliedDirective: AsRef<Directive>;
fn filter_directives<'dir, T: AsRef<Directive>>(
directives: impl IntoIterator<Item = &'dir T>,
name: &Name,
) -> Vec<&'dir T> {
directives
.into_iter()
.filter(|directive| &directive.as_ref().name == name)
.collect()
}
fn get_applied_directives<'schema>(
&self,
schema: &'schema FederationSchema,
directive_name: &Name,
) -> Vec<&'schema Self::AppliedDirective>;
}
impl HasAppliedDirectives for SchemaDefinitionPosition {
type AppliedDirective = Component<Directive>;
fn get_applied_directives<'schema>(
&self,
schema: &'schema FederationSchema,
directive_name: &Name,
) -> Vec<&'schema Component<Directive>> {
let schema_def = self.get(&schema.schema);
Self::filter_directives(schema_def.directives.iter(), directive_name)
}
}
impl HasAppliedDirectives for TypeDefinitionPosition {
type AppliedDirective = Component<Directive>;
fn get_applied_directives<'schema>(
&self,
schema: &'schema FederationSchema,
directive_name: &Name,
) -> Vec<&'schema Component<Directive>> {
match self {
TypeDefinitionPosition::Scalar(type_) => {
type_.get_applied_directives(schema, directive_name)
}
TypeDefinitionPosition::Object(type_) => {
type_.get_applied_directives(schema, directive_name)
}
TypeDefinitionPosition::Interface(type_) => {
type_.get_applied_directives(schema, directive_name)
}
TypeDefinitionPosition::Union(type_) => {
type_.get_applied_directives(schema, directive_name)
}
TypeDefinitionPosition::Enum(type_) => {
type_.get_applied_directives(schema, directive_name)
}
TypeDefinitionPosition::InputObject(type_) => {
type_.get_applied_directives(schema, directive_name)
}
}
}
}
impl HasAppliedDirectives for CompositeTypeDefinitionPosition {
type AppliedDirective = Component<Directive>;
fn get_applied_directives<'schema>(
&self,
schema: &'schema FederationSchema,
directive_name: &Name,
) -> Vec<&'schema Component<Directive>> {
match self {
CompositeTypeDefinitionPosition::Object(type_) => {
type_.get_applied_directives(schema, directive_name)
}
CompositeTypeDefinitionPosition::Interface(type_) => {
type_.get_applied_directives(schema, directive_name)
}
CompositeTypeDefinitionPosition::Union(type_) => {
type_.get_applied_directives(schema, directive_name)
}
}
}
}
impl HasAppliedDirectives for FieldDefinitionPosition {
type AppliedDirective = Node<Directive>;
fn get_applied_directives<'schema>(
&self,
schema: &'schema FederationSchema,
directive_name: &Name,
) -> Vec<&'schema Node<Directive>> {
match self {
FieldDefinitionPosition::Object(field) => {
field.get_applied_directives(schema, directive_name)
}
FieldDefinitionPosition::Interface(field) => {
field.get_applied_directives(schema, directive_name)
}
FieldDefinitionPosition::Union(_) => vec![],
}
}
}
impl HasAppliedDirectives for ObjectOrInterfaceFieldDefinitionPosition {
type AppliedDirective = Node<Directive>;
fn get_applied_directives<'schema>(
&self,
schema: &'schema FederationSchema,
directive_name: &Name,
) -> Vec<&'schema Node<Directive>> {
match self {
Self::Object(field) => field.get_applied_directives(schema, directive_name),
Self::Interface(field) => field.get_applied_directives(schema, directive_name),
}
}
}
impl HasAppliedDirectives for ObjectOrInterfaceTypeDefinitionPosition {
type AppliedDirective = Component<Directive>;
fn get_applied_directives<'schema>(
&self,
schema: &'schema FederationSchema,
directive_name: &Name,
) -> Vec<&'schema Component<Directive>> {
match self {
ObjectOrInterfaceTypeDefinitionPosition::Object(type_) => {
type_.get_applied_directives(schema, directive_name)
}
ObjectOrInterfaceTypeDefinitionPosition::Interface(type_) => {
type_.get_applied_directives(schema, directive_name)
}
}
}
}
impl HasAppliedDirectives for ScalarTypeDefinitionPosition {
type AppliedDirective = Component<Directive>;
fn get_applied_directives<'schema>(
&self,
schema: &'schema FederationSchema,
directive_name: &Name,
) -> Vec<&'schema Component<Directive>> {
self.try_get(&schema.schema)
.map(|scalar| Self::filter_directives(scalar.directives.iter(), directive_name))
.unwrap_or_default()
}
}
impl HasAppliedDirectives for ObjectTypeDefinitionPosition {
type AppliedDirective = Component<Directive>;
fn get_applied_directives<'schema>(
&self,
schema: &'schema FederationSchema,
directive_name: &Name,
) -> Vec<&'schema Component<Directive>> {
self.try_get(&schema.schema)
.map(|object| Self::filter_directives(object.directives.iter(), directive_name))
.unwrap_or_default()
}
}
impl HasAppliedDirectives for ObjectFieldDefinitionPosition {
type AppliedDirective = Node<Directive>;
fn get_applied_directives<'schema>(
&self,
schema: &'schema FederationSchema,
directive_name: &Name,
) -> Vec<&'schema Node<Directive>> {
self.try_get(&schema.schema)
.map(|field| Self::filter_directives(field.directives.iter(), directive_name))
.unwrap_or_default()
}
}
impl HasAppliedDirectives for ObjectFieldArgumentDefinitionPosition {
type AppliedDirective = Node<Directive>;
fn get_applied_directives<'schema>(
&self,
schema: &'schema FederationSchema,
directive_name: &Name,
) -> Vec<&'schema Node<Directive>> {
self.try_get(&schema.schema)
.map(|input| Self::filter_directives(input.directives.iter(), directive_name))
.unwrap_or_default()
}
}
impl HasAppliedDirectives for InterfaceTypeDefinitionPosition {
type AppliedDirective = Component<Directive>;
fn get_applied_directives<'schema>(
&self,
schema: &'schema FederationSchema,
directive_name: &Name,
) -> Vec<&'schema Component<Directive>> {
self.try_get(&schema.schema)
.map(|intf| Self::filter_directives(intf.directives.iter(), directive_name))
.unwrap_or_default()
}
}
impl HasAppliedDirectives for InterfaceFieldDefinitionPosition {
type AppliedDirective = Node<Directive>;
fn get_applied_directives<'schema>(
&self,
schema: &'schema FederationSchema,
directive_name: &Name,
) -> Vec<&'schema Node<Directive>> {
self.try_get(&schema.schema)
.map(|field| Self::filter_directives(field.directives.iter(), directive_name))
.unwrap_or_default()
}
}
impl HasAppliedDirectives for InterfaceFieldArgumentDefinitionPosition {
type AppliedDirective = Node<Directive>;
fn get_applied_directives<'schema>(
&self,
schema: &'schema FederationSchema,
directive_name: &Name,
) -> Vec<&'schema Node<Directive>> {
self.try_get(&schema.schema)
.map(|input| Self::filter_directives(input.directives.iter(), directive_name))
.unwrap_or_default()
}
}
impl HasAppliedDirectives for UnionTypeDefinitionPosition {
type AppliedDirective = Component<Directive>;
fn get_applied_directives<'schema>(
&self,
schema: &'schema FederationSchema,
directive_name: &Name,
) -> Vec<&'schema Component<Directive>> {
self.try_get(&schema.schema)
.map(|union| Self::filter_directives(union.directives.iter(), directive_name))
.unwrap_or_default()
}
}
impl HasAppliedDirectives for EnumTypeDefinitionPosition {
type AppliedDirective = Component<Directive>;
fn get_applied_directives<'schema>(
&self,
schema: &'schema FederationSchema,
directive_name: &Name,
) -> Vec<&'schema Component<Directive>> {
self.try_get(&schema.schema)
.map(|enum_type| Self::filter_directives(enum_type.directives.iter(), directive_name))
.unwrap_or_default()
}
}
impl HasAppliedDirectives for EnumValueDefinitionPosition {
type AppliedDirective = Node<Directive>;
fn get_applied_directives<'schema>(
&self,
schema: &'schema FederationSchema,
directive_name: &Name,
) -> Vec<&'schema Node<Directive>> {
self.try_get(&schema.schema)
.map(|value| Self::filter_directives(value.directives.iter(), directive_name))
.unwrap_or_default()
}
}
impl HasAppliedDirectives for InputObjectTypeDefinitionPosition {
type AppliedDirective = Component<Directive>;
fn get_applied_directives<'schema>(
&self,
schema: &'schema FederationSchema,
directive_name: &Name,
) -> Vec<&'schema Component<Directive>> {
self.try_get(&schema.schema)
.map(|input| Self::filter_directives(input.directives.iter(), directive_name))
.unwrap_or_default()
}
}
impl HasAppliedDirectives for InputObjectFieldDefinitionPosition {
type AppliedDirective = Node<Directive>;
fn get_applied_directives<'schema>(
&self,
schema: &'schema FederationSchema,
directive_name: &Name,
) -> Vec<&'schema Node<Directive>> {
self.try_get(&schema.schema)
.map(|field| Self::filter_directives(field.directives.iter(), directive_name))
.unwrap_or_default()
}
}
impl HasAppliedDirectives for DirectiveArgumentDefinitionPosition {
type AppliedDirective = Node<Directive>;
fn get_applied_directives<'schema>(
&self,
schema: &'schema FederationSchema,
directive_name: &Name,
) -> Vec<&'schema Node<Directive>> {
self.try_get(&schema.schema)
.map(|input| Self::filter_directives(input.directives.iter(), directive_name))
.unwrap_or_default()
}
}
impl HasAppliedDirectives for DirectiveTargetPosition {
type AppliedDirective = Node<Directive>;
fn get_applied_directives<'schema>(
&self,
schema: &'schema FederationSchema,
directive_name: &Name,
) -> Vec<&'schema Node<Directive>> {
match self {
Self::Schema(pos) => pos
.get_applied_directives(schema, directive_name)
.iter()
.map(|d| &d.node)
.collect(),
Self::ScalarType(pos) => pos
.get_applied_directives(schema, directive_name)
.iter()
.map(|d| &d.node)
.collect(),
Self::ObjectType(pos) => pos
.get_applied_directives(schema, directive_name)
.iter()
.map(|d| &d.node)
.collect(),
Self::ObjectField(pos) => pos.get_applied_directives(schema, directive_name),
Self::ObjectFieldArgument(pos) => pos.get_applied_directives(schema, directive_name),
Self::InterfaceType(pos) => pos
.get_applied_directives(schema, directive_name)
.iter()
.map(|d| &d.node)
.collect(),
Self::InterfaceField(pos) => pos.get_applied_directives(schema, directive_name),
Self::InterfaceFieldArgument(pos) => pos.get_applied_directives(schema, directive_name),
Self::UnionType(pos) => pos
.get_applied_directives(schema, directive_name)
.iter()
.map(|d| &d.node)
.collect(),
Self::EnumType(pos) => pos
.get_applied_directives(schema, directive_name)
.iter()
.map(|d| &d.node)
.collect(),
Self::EnumValue(pos) => pos.get_applied_directives(schema, directive_name),
Self::InputObjectType(pos) => pos
.get_applied_directives(schema, directive_name)
.iter()
.map(|d| &d.node)
.collect(),
Self::InputObjectField(pos) => pos.get_applied_directives(schema, directive_name),
Self::DirectiveArgument(pos) => pos.get_applied_directives(schema, directive_name),
}
}
}
#[derive(Clone, PartialEq, Eq, Hash, derive_more::From, derive_more::Display)]
pub(crate) enum TypeDefinitionPosition {
Scalar(ScalarTypeDefinitionPosition),
Object(ObjectTypeDefinitionPosition),
Interface(InterfaceTypeDefinitionPosition),
Union(UnionTypeDefinitionPosition),
Enum(EnumTypeDefinitionPosition),
InputObject(InputObjectTypeDefinitionPosition),
}
impl Debug for TypeDefinitionPosition {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
Self::Scalar(p) => write!(f, "Scalar({p})"),
Self::Object(p) => write!(f, "Object({p})"),
Self::Interface(p) => write!(f, "Interface({p})"),
Self::Union(p) => write!(f, "Union({p})"),
Self::Enum(p) => write!(f, "Enum({p})"),
Self::InputObject(p) => write!(f, "InputObject({p})"),
}
}
}
impl TypeDefinitionPosition {
pub(crate) fn is_composite_type(&self) -> bool {
matches!(
self,
TypeDefinitionPosition::Object(_)
| TypeDefinitionPosition::Interface(_)
| TypeDefinitionPosition::Union(_)
)
}
pub(crate) fn is_introspection_type(&self) -> bool {
self.type_name().starts_with("__")
}
pub(crate) fn is_object_type(&self) -> bool {
matches!(self, TypeDefinitionPosition::Object(_))
}
pub(crate) fn type_name(&self) -> &Name {
match self {
TypeDefinitionPosition::Scalar(type_) => &type_.type_name,
TypeDefinitionPosition::Object(type_) => &type_.type_name,
TypeDefinitionPosition::Interface(type_) => &type_.type_name,
TypeDefinitionPosition::Union(type_) => &type_.type_name,
TypeDefinitionPosition::Enum(type_) => &type_.type_name,
TypeDefinitionPosition::InputObject(type_) => &type_.type_name,
}
}
pub(crate) fn kind(&self) -> &'static str {
match self {
TypeDefinitionPosition::Object(_) => "ObjectType",
TypeDefinitionPosition::Interface(_) => "InterfaceType",
TypeDefinitionPosition::Union(_) => "UnionType",
TypeDefinitionPosition::Enum(_) => "EnumType",
TypeDefinitionPosition::Scalar(_) => "ScalarType",
TypeDefinitionPosition::InputObject(_) => "InputObjectType",
}
}
fn describe(&self) -> &'static str {
match self {
TypeDefinitionPosition::Scalar(_) => ScalarTypeDefinitionPosition::EXPECTED,
TypeDefinitionPosition::Object(_) => ObjectTypeDefinitionPosition::EXPECTED,
TypeDefinitionPosition::Interface(_) => InterfaceTypeDefinitionPosition::EXPECTED,
TypeDefinitionPosition::Union(_) => UnionTypeDefinitionPosition::EXPECTED,
TypeDefinitionPosition::Enum(_) => EnumTypeDefinitionPosition::EXPECTED,
TypeDefinitionPosition::InputObject(_) => InputObjectTypeDefinitionPosition::EXPECTED,
}
}
pub(crate) fn get<'schema>(
&self,
schema: &'schema Schema,
) -> Result<&'schema ExtendedType, PositionLookupError> {
let name = self.type_name();
let ty = schema
.types
.get(name)
.ok_or_else(|| PositionLookupError::TypeMissing(name.clone()))?;
match (ty, self) {
(ExtendedType::Scalar(_), TypeDefinitionPosition::Scalar(_))
| (ExtendedType::Object(_), TypeDefinitionPosition::Object(_))
| (ExtendedType::Interface(_), TypeDefinitionPosition::Interface(_))
| (ExtendedType::Union(_), TypeDefinitionPosition::Union(_))
| (ExtendedType::Enum(_), TypeDefinitionPosition::Enum(_))
| (ExtendedType::InputObject(_), TypeDefinitionPosition::InputObject(_)) => Ok(ty),
_ => Err(PositionLookupError::TypeWrongKind(
name.clone(),
self.describe(),
)),
}
}
pub(crate) fn insert_directive(
&self,
schema: &mut FederationSchema,
directive: Component<Directive>,
) -> Result<(), FederationError> {
match self {
TypeDefinitionPosition::Scalar(type_) => type_.insert_directive(schema, directive),
TypeDefinitionPosition::Object(type_) => type_.insert_directive(schema, directive),
TypeDefinitionPosition::Interface(type_) => type_.insert_directive(schema, directive),
TypeDefinitionPosition::Union(type_) => type_.insert_directive(schema, directive),
TypeDefinitionPosition::Enum(type_) => type_.insert_directive(schema, directive),
TypeDefinitionPosition::InputObject(type_) => type_.insert_directive(schema, directive),
}
}
pub(crate) fn rename(
&self,
schema: &mut FederationSchema,
new_name: Name,
) -> Result<(), FederationError> {
let old_name = self.type_name().clone();
match self {
TypeDefinitionPosition::Scalar(type_) => type_.rename(schema, new_name.clone())?,
TypeDefinitionPosition::Object(type_) => type_.rename(schema, new_name.clone())?,
TypeDefinitionPosition::Interface(type_) => type_.rename(schema, new_name.clone())?,
TypeDefinitionPosition::Union(type_) => type_.rename(schema, new_name.clone())?,
TypeDefinitionPosition::Enum(type_) => type_.rename(schema, new_name.clone())?,
TypeDefinitionPosition::InputObject(type_) => type_.rename(schema, new_name.clone())?,
}
if let Some(existing_type) = schema.schema.types.swap_remove(self.type_name()) {
schema.schema.types.insert(new_name.clone(), existing_type);
}
match self {
TypeDefinitionPosition::Object(_) => {
schema.referencers.rename_object_type(&old_name, &new_name);
}
TypeDefinitionPosition::Interface(_) => {
schema
.referencers
.rename_interface_type(&old_name, &new_name);
}
_ => {}
}
Ok(())
}
pub(crate) fn remove_extensions(
&self,
schema: &mut FederationSchema,
) -> Result<(), FederationError> {
match self {
TypeDefinitionPosition::Scalar(type_) => type_.remove_extensions(schema),
TypeDefinitionPosition::Object(type_) => type_.remove_extensions(schema),
TypeDefinitionPosition::Interface(type_) => type_.remove_extensions(schema),
TypeDefinitionPosition::Union(type_) => type_.remove_extensions(schema),
TypeDefinitionPosition::Enum(type_) => type_.remove_extensions(schema),
TypeDefinitionPosition::InputObject(type_) => type_.remove_extensions(schema),
}
}
pub(crate) fn has_applied_directive(
&self,
schema: &FederationSchema,
directive_name: &Name,
) -> bool {
match self {
TypeDefinitionPosition::Scalar(type_) => {
type_.has_applied_directive(schema, directive_name)
}
TypeDefinitionPosition::Object(type_) => {
type_.has_applied_directive(schema, directive_name)
}
TypeDefinitionPosition::Interface(type_) => {
type_.has_applied_directive(schema, directive_name)
}
TypeDefinitionPosition::Union(type_) => {
type_.has_applied_directive(schema, directive_name)
}
TypeDefinitionPosition::Enum(type_) => {
type_.has_applied_directive(schema, directive_name)
}
TypeDefinitionPosition::InputObject(type_) => {
type_.has_applied_directive(schema, directive_name)
}
}
}
#[allow(unused)]
pub(crate) fn remove_directive(
&self,
schema: &mut FederationSchema,
directive: &Component<Directive>,
) {
match self {
TypeDefinitionPosition::Scalar(type_) => type_.remove_directive(schema, directive),
TypeDefinitionPosition::Object(type_) => type_.remove_directive(schema, directive),
TypeDefinitionPosition::Interface(type_) => type_.remove_directive(schema, directive),
TypeDefinitionPosition::Union(type_) => type_.remove_directive(schema, directive),
TypeDefinitionPosition::Enum(type_) => type_.remove_directive(schema, directive),
TypeDefinitionPosition::InputObject(type_) => type_.remove_directive(schema, directive),
}
}
pub(crate) fn pre_insert(&self, schema: &mut FederationSchema) -> Result<(), FederationError> {
match self {
TypeDefinitionPosition::Scalar(type_) => type_.pre_insert(schema),
TypeDefinitionPosition::Object(type_) => type_.pre_insert(schema),
TypeDefinitionPosition::Interface(type_) => type_.pre_insert(schema),
TypeDefinitionPosition::Union(type_) => type_.pre_insert(schema),
TypeDefinitionPosition::Enum(type_) => type_.pre_insert(schema),
TypeDefinitionPosition::InputObject(type_) => type_.pre_insert(schema),
}
}
pub(crate) fn insert_empty(
&self,
schema: &mut FederationSchema,
) -> Result<(), FederationError> {
match self {
TypeDefinitionPosition::Scalar(type_) => type_.insert(
schema,
Node::new(ScalarType {
description: None,
name: self.type_name().clone(),
directives: Default::default(),
}),
),
TypeDefinitionPosition::Object(type_) => type_.insert(
schema,
Node::new(ObjectType {
description: None,
name: self.type_name().clone(),
implements_interfaces: Default::default(),
fields: Default::default(),
directives: Default::default(),
}),
),
TypeDefinitionPosition::Interface(type_) => type_.insert_empty(schema),
TypeDefinitionPosition::Union(type_) => type_.insert(
schema,
Node::new(UnionType {
description: None,
name: self.type_name().clone(),
members: Default::default(),
directives: Default::default(),
}),
),
TypeDefinitionPosition::Enum(type_) => type_.insert(
schema,
Node::new(EnumType {
description: None,
name: self.type_name().clone(),
values: Default::default(),
directives: Default::default(),
}),
),
TypeDefinitionPosition::InputObject(type_) => type_.insert(
schema,
Node::new(InputObjectType {
description: None,
name: self.type_name().clone(),
fields: Default::default(),
directives: Default::default(),
}),
),
}
}
#[allow(unused)]
pub(crate) fn remove(&self, schema: &mut FederationSchema) -> Result<bool, FederationError> {
let is_some = match self {
TypeDefinitionPosition::Scalar(scalar_pos) => scalar_pos.remove(schema)?.is_some(),
TypeDefinitionPosition::Enum(enum_pos) => enum_pos.remove(schema)?.is_some(),
TypeDefinitionPosition::Object(object_pos) => object_pos.remove(schema)?.is_some(),
TypeDefinitionPosition::Interface(interface_pos) => {
interface_pos.remove(schema)?.is_some()
}
TypeDefinitionPosition::Union(union_pos) => union_pos.remove(schema)?.is_some(),
TypeDefinitionPosition::InputObject(input_object_pos) => {
input_object_pos.remove(schema)?.is_some()
}
};
Ok(is_some)
}
#[allow(unused)]
pub(crate) fn remove_recursive(
&self,
schema: &mut FederationSchema,
) -> Result<(), FederationError> {
match self {
TypeDefinitionPosition::Scalar(scalar_pos) => {
_ = scalar_pos.remove(schema)?;
}
TypeDefinitionPosition::Enum(enum_pos) => {
_ = enum_pos.remove(schema)?;
}
TypeDefinitionPosition::Object(object_pos) => {
object_pos.remove_recursive(schema)?;
}
TypeDefinitionPosition::Interface(interface_pos) => {
interface_pos.remove_recursive(schema)?;
}
TypeDefinitionPosition::Union(union_pos) => {
union_pos.remove_recursive(schema)?;
}
TypeDefinitionPosition::InputObject(input_object_pos) => {
input_object_pos.remove_recursive(schema)?;
}
};
Ok(())
}
}
impl From<&ExtendedType> for TypeDefinitionPosition {
fn from(ty: &ExtendedType) -> Self {
match ty {
ExtendedType::Scalar(v) => {
TypeDefinitionPosition::Scalar(ScalarTypeDefinitionPosition {
type_name: v.name.clone(),
})
}
ExtendedType::Object(v) => {
TypeDefinitionPosition::Object(ObjectTypeDefinitionPosition {
type_name: v.name.clone(),
})
}
ExtendedType::Interface(v) => {
TypeDefinitionPosition::Interface(InterfaceTypeDefinitionPosition {
type_name: v.name.clone(),
})
}
ExtendedType::Union(v) => TypeDefinitionPosition::Union(UnionTypeDefinitionPosition {
type_name: v.name.clone(),
}),
ExtendedType::Enum(v) => TypeDefinitionPosition::Enum(EnumTypeDefinitionPosition {
type_name: v.name.clone(),
}),
ExtendedType::InputObject(v) => {
TypeDefinitionPosition::InputObject(InputObjectTypeDefinitionPosition {
type_name: v.name.clone(),
})
}
}
}
}
impl HasDescription for TypeDefinitionPosition {
fn description<'schema>(
&self,
schema: &'schema FederationSchema,
) -> Option<&'schema Node<str>> {
match self {
Self::Scalar(ty) => ty.description(schema),
Self::Object(ty) => ty.description(schema),
Self::Interface(ty) => ty.description(schema),
Self::Union(ty) => ty.description(schema),
Self::Enum(ty) => ty.description(schema),
Self::InputObject(ty) => ty.description(schema),
}
}
fn set_description(
&self,
schema: &mut FederationSchema,
description: Option<Node<str>>,
) -> Result<(), FederationError> {
match self {
Self::Scalar(ty) => ty.set_description(schema, description),
Self::Object(ty) => ty.set_description(schema, description),
Self::Interface(ty) => ty.set_description(schema, description),
Self::Union(ty) => ty.set_description(schema, description),
Self::Enum(ty) => ty.set_description(schema, description),
Self::InputObject(ty) => ty.set_description(schema, description),
}
}
fn is_schema_definition() -> bool {
false
}
}
fallible_conversions!(TypeDefinitionPosition::Scalar -> ScalarTypeDefinitionPosition);
fallible_conversions!(TypeDefinitionPosition::Object -> ObjectTypeDefinitionPosition);
fallible_conversions!(TypeDefinitionPosition::Interface -> InterfaceTypeDefinitionPosition);
fallible_conversions!(TypeDefinitionPosition::Union -> UnionTypeDefinitionPosition);
fallible_conversions!(TypeDefinitionPosition::Enum -> EnumTypeDefinitionPosition);
fallible_conversions!(TypeDefinitionPosition::InputObject -> InputObjectTypeDefinitionPosition);
infallible_conversions!(OutputTypeDefinitionPosition::{Scalar, Object, Interface, Union, Enum} -> TypeDefinitionPosition);
infallible_conversions!(CompositeTypeDefinitionPosition::{Object, Interface, Union} -> TypeDefinitionPosition);
infallible_conversions!(AbstractTypeDefinitionPosition::{Interface, Union} -> TypeDefinitionPosition);
infallible_conversions!(ObjectOrInterfaceTypeDefinitionPosition::{Object, Interface} -> TypeDefinitionPosition);
#[derive(Clone, PartialEq, Eq, Hash, derive_more::From, derive_more::Display)]
pub(crate) enum OutputTypeDefinitionPosition {
Scalar(ScalarTypeDefinitionPosition),
Object(ObjectTypeDefinitionPosition),
Interface(InterfaceTypeDefinitionPosition),
Union(UnionTypeDefinitionPosition),
Enum(EnumTypeDefinitionPosition),
}
impl Debug for OutputTypeDefinitionPosition {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
Self::Scalar(p) => write!(f, "Scalar({p})"),
Self::Object(p) => write!(f, "Object({p})"),
Self::Interface(p) => write!(f, "Interface({p})"),
Self::Union(p) => write!(f, "Union({p})"),
Self::Enum(p) => write!(f, "Enum({p})"),
}
}
}
impl OutputTypeDefinitionPosition {
const EXPECTED: &'static str = "an output type";
pub(crate) fn is_leaf_type(&self) -> bool {
matches!(
self,
OutputTypeDefinitionPosition::Scalar(_) | OutputTypeDefinitionPosition::Enum(_)
)
}
pub(crate) fn type_name(&self) -> &Name {
match self {
OutputTypeDefinitionPosition::Scalar(type_) => &type_.type_name,
OutputTypeDefinitionPosition::Object(type_) => &type_.type_name,
OutputTypeDefinitionPosition::Interface(type_) => &type_.type_name,
OutputTypeDefinitionPosition::Union(type_) => &type_.type_name,
OutputTypeDefinitionPosition::Enum(type_) => &type_.type_name,
}
}
}
fallible_conversions!(OutputTypeDefinitionPosition::Scalar -> ScalarTypeDefinitionPosition);
fallible_conversions!(OutputTypeDefinitionPosition::Object -> ObjectTypeDefinitionPosition);
fallible_conversions!(OutputTypeDefinitionPosition::Interface -> InterfaceTypeDefinitionPosition);
fallible_conversions!(OutputTypeDefinitionPosition::Union -> UnionTypeDefinitionPosition);
fallible_conversions!(OutputTypeDefinitionPosition::Enum -> EnumTypeDefinitionPosition);
fallible_conversions!(TypeDefinitionPosition::{Scalar, Object, Interface, Enum, Union} -> OutputTypeDefinitionPosition);
infallible_conversions!(CompositeTypeDefinitionPosition::{Object, Interface, Union} -> OutputTypeDefinitionPosition);
infallible_conversions!(AbstractTypeDefinitionPosition::{Interface, Union} -> OutputTypeDefinitionPosition);
infallible_conversions!(ObjectOrInterfaceTypeDefinitionPosition::{Object, Interface} -> OutputTypeDefinitionPosition);
#[derive(Clone, PartialEq, Eq, Hash, derive_more::From, derive_more::Display, Serialize)]
pub(crate) enum CompositeTypeDefinitionPosition {
Object(ObjectTypeDefinitionPosition),
Interface(InterfaceTypeDefinitionPosition),
Union(UnionTypeDefinitionPosition),
}
impl Debug for CompositeTypeDefinitionPosition {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
Self::Object(p) => write!(f, "Object({p})"),
Self::Interface(p) => write!(f, "Interface({p})"),
Self::Union(p) => write!(f, "Union({p})"),
}
}
}
impl CompositeTypeDefinitionPosition {
const EXPECTED: &'static str = "a composite type";
pub(crate) fn is_object_type(&self) -> bool {
matches!(self, CompositeTypeDefinitionPosition::Object(_))
}
pub(crate) fn is_interface_type(&self) -> bool {
matches!(self, CompositeTypeDefinitionPosition::Interface(_))
}
pub(crate) fn is_union_type(&self) -> bool {
matches!(self, CompositeTypeDefinitionPosition::Union(_))
}
pub(crate) fn is_abstract_type(&self) -> bool {
self.is_interface_type() || self.is_union_type()
}
pub(crate) fn type_name(&self) -> &Name {
match self {
CompositeTypeDefinitionPosition::Object(type_) => &type_.type_name,
CompositeTypeDefinitionPosition::Interface(type_) => &type_.type_name,
CompositeTypeDefinitionPosition::Union(type_) => &type_.type_name,
}
}
fn describe(&self) -> &'static str {
match self {
CompositeTypeDefinitionPosition::Object(_) => ObjectTypeDefinitionPosition::EXPECTED,
CompositeTypeDefinitionPosition::Interface(_) => {
InterfaceTypeDefinitionPosition::EXPECTED
}
CompositeTypeDefinitionPosition::Union(_) => UnionTypeDefinitionPosition::EXPECTED,
}
}
pub(crate) fn field(
&self,
field_name: Name,
) -> Result<FieldDefinitionPosition, FederationError> {
match self {
CompositeTypeDefinitionPosition::Object(type_) => Ok(type_.field(field_name).into()),
CompositeTypeDefinitionPosition::Interface(type_) => Ok(type_.field(field_name).into()),
CompositeTypeDefinitionPosition::Union(type_) => {
let field = type_.introspection_typename_field();
if *field.field_name() == field_name {
Ok(field.into())
} else {
Err(FederationError::internal(format!(
r#"Union types don't have field "{}", only "{}""#,
field_name,
field.field_name(),
)))
}
}
}
}
pub(crate) fn introspection_typename_field(&self) -> FieldDefinitionPosition {
match self {
CompositeTypeDefinitionPosition::Object(type_) => {
type_.introspection_typename_field().into()
}
CompositeTypeDefinitionPosition::Interface(type_) => {
type_.introspection_typename_field().into()
}
CompositeTypeDefinitionPosition::Union(type_) => {
type_.introspection_typename_field().into()
}
}
}
pub(crate) fn get<'schema>(
&self,
schema: &'schema Schema,
) -> Result<&'schema ExtendedType, PositionLookupError> {
let name = self.type_name();
let ty = schema
.types
.get(name)
.ok_or_else(|| PositionLookupError::TypeMissing(name.clone()))?;
match (ty, self) {
(ExtendedType::Object(_), CompositeTypeDefinitionPosition::Object(_))
| (ExtendedType::Interface(_), CompositeTypeDefinitionPosition::Interface(_))
| (ExtendedType::Union(_), CompositeTypeDefinitionPosition::Union(_)) => Ok(ty),
_ => Err(PositionLookupError::TypeWrongKind(
name.clone(),
self.describe(),
)),
}
}
pub(crate) fn insert_directive(
&self,
schema: &mut FederationSchema,
directive: Component<Directive>,
) -> Result<(), FederationError> {
match self {
CompositeTypeDefinitionPosition::Object(type_) => {
type_.insert_directive(schema, directive)
}
CompositeTypeDefinitionPosition::Interface(type_) => {
type_.insert_directive(schema, directive)
}
CompositeTypeDefinitionPosition::Union(type_) => {
type_.insert_directive(schema, directive)
}
}
}
}
fallible_conversions!(CompositeTypeDefinitionPosition::Object -> ObjectTypeDefinitionPosition);
fallible_conversions!(CompositeTypeDefinitionPosition::Interface -> InterfaceTypeDefinitionPosition);
fallible_conversions!(CompositeTypeDefinitionPosition::Union -> UnionTypeDefinitionPosition);
fallible_conversions!(TypeDefinitionPosition::{Object, Interface, Union} -> CompositeTypeDefinitionPosition);
fallible_conversions!(OutputTypeDefinitionPosition::{Object, Interface, Union} -> CompositeTypeDefinitionPosition);
infallible_conversions!(AbstractTypeDefinitionPosition::{Interface, Union} -> CompositeTypeDefinitionPosition);
infallible_conversions!(ObjectOrInterfaceTypeDefinitionPosition::{Object, Interface} -> CompositeTypeDefinitionPosition);
#[derive(Clone, PartialEq, Eq, Hash, derive_more::From, derive_more::Display)]
pub(crate) enum AbstractTypeDefinitionPosition {
Interface(InterfaceTypeDefinitionPosition),
Union(UnionTypeDefinitionPosition),
}
impl Debug for AbstractTypeDefinitionPosition {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
Self::Interface(p) => write!(f, "Interface({p})"),
Self::Union(p) => write!(f, "Union({p})"),
}
}
}
impl AbstractTypeDefinitionPosition {
const EXPECTED: &'static str = "an abstract type";
pub(crate) fn type_name(&self) -> &Name {
match self {
AbstractTypeDefinitionPosition::Interface(type_) => &type_.type_name,
AbstractTypeDefinitionPosition::Union(type_) => &type_.type_name,
}
}
}
fallible_conversions!(AbstractTypeDefinitionPosition::Interface -> InterfaceTypeDefinitionPosition);
fallible_conversions!(AbstractTypeDefinitionPosition::Union -> UnionTypeDefinitionPosition);
fallible_conversions!(TypeDefinitionPosition::{Interface, Union} -> AbstractTypeDefinitionPosition);
fallible_conversions!(OutputTypeDefinitionPosition::{Interface, Union} -> AbstractTypeDefinitionPosition);
fallible_conversions!(CompositeTypeDefinitionPosition::{Interface, Union} -> AbstractTypeDefinitionPosition);
fallible_conversions!(ObjectOrInterfaceTypeDefinitionPosition::{Interface} -> AbstractTypeDefinitionPosition);
#[derive(Clone, PartialEq, Eq, Hash, derive_more::From, derive_more::Display)]
pub(crate) enum ObjectOrInterfaceTypeDefinitionPosition {
Object(ObjectTypeDefinitionPosition),
Interface(InterfaceTypeDefinitionPosition),
}
impl Debug for ObjectOrInterfaceTypeDefinitionPosition {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
Self::Object(p) => write!(f, "Object({p})"),
Self::Interface(p) => write!(f, "Interface({p})"),
}
}
}
impl ObjectOrInterfaceTypeDefinitionPosition {
const EXPECTED: &'static str = "an object/interface type";
pub(crate) fn type_name(&self) -> &Name {
match self {
ObjectOrInterfaceTypeDefinitionPosition::Object(type_) => &type_.type_name,
ObjectOrInterfaceTypeDefinitionPosition::Interface(type_) => &type_.type_name,
}
}
pub(crate) fn field(&self, field_name: Name) -> ObjectOrInterfaceFieldDefinitionPosition {
match self {
ObjectOrInterfaceTypeDefinitionPosition::Object(type_) => {
type_.field(field_name).into()
}
ObjectOrInterfaceTypeDefinitionPosition::Interface(type_) => {
type_.field(field_name).into()
}
}
}
pub(crate) fn fields<'a>(
&'a self,
schema: &'a Schema,
) -> Result<impl Iterator<Item = ObjectOrInterfaceFieldDefinitionPosition>, FederationError>
{
match self {
ObjectOrInterfaceTypeDefinitionPosition::Object(type_) => Ok(Either::Left(
type_.fields(schema)?.map(|field| field.into()),
)),
ObjectOrInterfaceTypeDefinitionPosition::Interface(type_) => Ok(Either::Right(
type_.fields(schema)?.map(|field| field.into()),
)),
}
}
pub(crate) fn insert_directive(
&self,
schema: &mut FederationSchema,
directive: Component<Directive>,
) -> Result<(), FederationError> {
match self {
Self::Object(type_) => type_.insert_directive(schema, directive),
Self::Interface(type_) => type_.insert_directive(schema, directive),
}
}
pub(crate) fn implemented_interfaces<'schema>(
&self,
schema: &'schema FederationSchema,
) -> Result<&'schema apollo_compiler::collections::IndexSet<ComponentName>, PositionLookupError>
{
match self {
Self::Object(type_) => type_
.get(schema.schema())
.map(|obj| &obj.implements_interfaces),
Self::Interface(type_) => type_
.get(schema.schema())
.map(|itf| &itf.implements_interfaces),
}
}
pub(crate) fn insert_implements_interface(
&self,
schema: &mut FederationSchema,
interface_name: ComponentName,
) -> Result<(), FederationError> {
match self {
Self::Object(type_) => type_.insert_implements_interface(schema, interface_name),
Self::Interface(type_) => type_.insert_implements_interface(schema, interface_name),
}
}
}
fallible_conversions!(ObjectOrInterfaceTypeDefinitionPosition::Object -> ObjectTypeDefinitionPosition);
fallible_conversions!(ObjectOrInterfaceTypeDefinitionPosition::Interface -> InterfaceTypeDefinitionPosition);
fallible_conversions!(TypeDefinitionPosition::{Object, Interface} -> ObjectOrInterfaceTypeDefinitionPosition);
fallible_conversions!(OutputTypeDefinitionPosition::{Object, Interface} -> ObjectOrInterfaceTypeDefinitionPosition);
fallible_conversions!(CompositeTypeDefinitionPosition::{Object, Interface} -> ObjectOrInterfaceTypeDefinitionPosition);
fallible_conversions!(AbstractTypeDefinitionPosition::{Interface} -> ObjectOrInterfaceTypeDefinitionPosition);
#[derive(Clone, PartialEq, Eq, Hash, derive_more::From, derive_more::Display, Serialize)]
pub(crate) enum FieldDefinitionPosition {
Object(ObjectFieldDefinitionPosition),
Interface(InterfaceFieldDefinitionPosition),
Union(UnionTypenameFieldDefinitionPosition),
}
impl Debug for FieldDefinitionPosition {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
Self::Object(p) => write!(f, "Object({p})"),
Self::Interface(p) => write!(f, "Interface({p})"),
Self::Union(p) => write!(f, "Union({p})"),
}
}
}
impl FieldDefinitionPosition {
pub(crate) fn type_name(&self) -> &Name {
match self {
FieldDefinitionPosition::Object(field) => &field.type_name,
FieldDefinitionPosition::Interface(field) => &field.type_name,
FieldDefinitionPosition::Union(field) => &field.type_name,
}
}
pub(crate) fn field_name(&self) -> &Name {
match self {
FieldDefinitionPosition::Object(field) => &field.field_name,
FieldDefinitionPosition::Interface(field) => &field.field_name,
FieldDefinitionPosition::Union(field) => field.field_name(),
}
}
pub(crate) fn is_introspection_typename_field(&self) -> bool {
*self.field_name() == *INTROSPECTION_TYPENAME_FIELD_NAME
}
pub(crate) fn parent(&self) -> CompositeTypeDefinitionPosition {
match self {
FieldDefinitionPosition::Object(field) => field.parent().into(),
FieldDefinitionPosition::Interface(field) => field.parent().into(),
FieldDefinitionPosition::Union(field) => field.parent().into(),
}
}
#[allow(unused)]
pub(crate) fn has_applied_directive(
&self,
schema: &FederationSchema,
directive_name: &Name,
) -> bool {
match self {
FieldDefinitionPosition::Object(field) => !field
.get_applied_directives(schema, directive_name)
.is_empty(),
FieldDefinitionPosition::Interface(field) => !field
.get_applied_directives(schema, directive_name)
.is_empty(),
FieldDefinitionPosition::Union(_) => false,
}
}
pub(crate) fn remove_directive(
&self,
schema: &mut FederationSchema,
directive: &Node<Directive>,
) {
match self {
FieldDefinitionPosition::Object(field) => field.remove_directive(schema, directive),
FieldDefinitionPosition::Interface(field) => field.remove_directive(schema, directive),
FieldDefinitionPosition::Union(_) => (),
}
}
pub(crate) fn get<'schema>(
&self,
schema: &'schema Schema,
) -> Result<&'schema Component<FieldDefinition>, PositionLookupError> {
match self {
FieldDefinitionPosition::Object(field) => field.get(schema),
FieldDefinitionPosition::Interface(field) => field.get(schema),
FieldDefinitionPosition::Union(field) => field.get(schema),
}
}
pub(crate) fn try_get<'schema>(
&self,
schema: &'schema Schema,
) -> Option<&'schema Component<FieldDefinition>> {
self.get(schema).ok()
}
pub(crate) fn is_interface(&self) -> bool {
matches!(self, FieldDefinitionPosition::Interface(_))
}
}
infallible_conversions!(ObjectOrInterfaceFieldDefinitionPosition::{Object, Interface} -> FieldDefinitionPosition);
impl TryFrom<DirectiveTargetPosition> for FieldDefinitionPosition {
type Error = &'static str;
fn try_from(dl: DirectiveTargetPosition) -> Result<Self, Self::Error> {
match dl {
DirectiveTargetPosition::ObjectField(field) => {
Ok(FieldDefinitionPosition::Object(field))
}
DirectiveTargetPosition::InterfaceField(field) => {
Ok(FieldDefinitionPosition::Interface(field))
}
_ => Err("No valid conversion"),
}
}
}
#[derive(Clone, PartialEq, Eq, Hash, derive_more::From, derive_more::Display)]
pub(crate) enum ObjectOrInterfaceFieldDefinitionPosition {
Object(ObjectFieldDefinitionPosition),
Interface(InterfaceFieldDefinitionPosition),
}
impl Debug for ObjectOrInterfaceFieldDefinitionPosition {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
Self::Object(p) => write!(f, "Object({p})"),
Self::Interface(p) => write!(f, "Interface({p})"),
}
}
}
impl ObjectOrInterfaceFieldDefinitionPosition {
const EXPECTED: &'static str = "an object/interface field";
pub(crate) fn type_name(&self) -> &Name {
match self {
ObjectOrInterfaceFieldDefinitionPosition::Object(field) => &field.type_name,
ObjectOrInterfaceFieldDefinitionPosition::Interface(field) => &field.type_name,
}
}
pub(crate) fn field_name(&self) -> &Name {
match self {
ObjectOrInterfaceFieldDefinitionPosition::Object(field) => &field.field_name,
ObjectOrInterfaceFieldDefinitionPosition::Interface(field) => &field.field_name,
}
}
pub(crate) fn parent(&self) -> ObjectOrInterfaceTypeDefinitionPosition {
match self {
ObjectOrInterfaceFieldDefinitionPosition::Object(field) => field.parent().into(),
ObjectOrInterfaceFieldDefinitionPosition::Interface(field) => field.parent().into(),
}
}
pub(crate) fn get<'schema>(
&self,
schema: &'schema Schema,
) -> Result<&'schema Component<FieldDefinition>, PositionLookupError> {
match self {
ObjectOrInterfaceFieldDefinitionPosition::Object(field) => field.get(schema),
ObjectOrInterfaceFieldDefinitionPosition::Interface(field) => field.get(schema),
}
}
pub(crate) fn try_get<'schema>(
&self,
schema: &'schema Schema,
) -> Option<&'schema Component<FieldDefinition>> {
self.get(schema).ok()
}
pub(crate) fn insert_directive(
&self,
schema: &mut FederationSchema,
directive: Node<Directive>,
) -> Result<(), FederationError> {
match self {
ObjectOrInterfaceFieldDefinitionPosition::Object(field) => {
field.insert_directive(schema, directive)
}
ObjectOrInterfaceFieldDefinitionPosition::Interface(field) => {
field.insert_directive(schema, directive)
}
}
}
pub(crate) fn has_applied_directive(
&self,
schema: &FederationSchema,
directive_name: &Name,
) -> bool {
!self
.get_applied_directives(schema, directive_name)
.is_empty()
}
pub(crate) fn remove_directive(
&self,
schema: &mut FederationSchema,
directive: &Node<Directive>,
) {
match self {
ObjectOrInterfaceFieldDefinitionPosition::Object(field) => {
field.remove_directive(schema, directive)
}
ObjectOrInterfaceFieldDefinitionPosition::Interface(field) => {
field.remove_directive(schema, directive)
}
}
}
pub(crate) fn coordinate(&self) -> String {
format!("{}.{}", self.type_name(), self.field_name())
}
pub(crate) fn insert(
&self,
schema: &mut FederationSchema,
field_def: Component<FieldDefinition>,
) -> Result<(), FederationError> {
match self {
Self::Object(field) => field.insert(schema, field_def),
Self::Interface(field) => field.insert(schema, field_def),
}
}
}
impl HasArguments for ObjectOrInterfaceFieldDefinitionPosition {
type ArgumentPosition = FieldArgumentDefinitionPosition;
fn argument_position(&self, name: Name) -> Self::ArgumentPosition {
match self {
Self::Object(field) => field.argument_position(name).into(),
Self::Interface(field) => field.argument_position(name).into(),
}
}
fn get_argument<'schema>(
&self,
schema: &'schema FederationSchema,
name: &Name,
) -> Option<&'schema Node<InputValueDefinition>> {
match self {
Self::Object(field) => field.get_argument(schema, name),
Self::Interface(field) => field.get_argument(schema, name),
}
}
fn get_arguments<'schema>(
&self,
schema: &'schema FederationSchema,
) -> Result<&'schema Vec<Node<InputValueDefinition>>, FederationError> {
match self {
Self::Object(field) => field.get_arguments(schema),
Self::Interface(field) => field.get_arguments(schema),
}
}
fn insert_argument(
&self,
schema: &mut FederationSchema,
arg: Node<InputValueDefinition>,
) -> Result<(), FederationError> {
match self {
Self::Object(field) => field.insert_argument(schema, arg),
Self::Interface(field) => field.insert_argument(schema, arg),
}
}
fn remove_argument(
&self,
schema: &mut FederationSchema,
name: &Name,
) -> Result<(), FederationError> {
match self {
Self::Object(field) => field.remove_argument(schema, name),
Self::Interface(field) => field.remove_argument(schema, name),
}
}
}
fallible_conversions!(FieldDefinitionPosition::{Object, Interface} -> ObjectOrInterfaceFieldDefinitionPosition);
#[derive(Debug, Clone, PartialEq, Eq, Hash, derive_more::Display)]
pub(crate) struct SchemaDefinitionPosition;
impl SchemaDefinitionPosition {
pub(crate) fn get_all_applied_directives<'schema>(
&self,
schema: &'schema FederationSchema,
) -> impl Iterator<Item = &'schema Component<Directive>> {
self.get(&schema.schema).directives.iter()
}
pub(crate) fn get<'schema>(&self, schema: &'schema Schema) -> &'schema Node<SchemaDefinition> {
&schema.schema_definition
}
fn make_mut<'schema>(
&self,
schema: &'schema mut Schema,
) -> &'schema mut Node<SchemaDefinition> {
&mut schema.schema_definition
}
pub(crate) fn insert_directive(
&self,
schema: &mut FederationSchema,
directive: Component<Directive>,
) -> Result<(), FederationError> {
self.insert_directive_at(schema, directive, self.get(&schema.schema).directives.len())
}
pub(crate) fn insert_directive_at(
&self,
schema: &mut FederationSchema,
directive: Component<Directive>,
index: usize,
) -> Result<(), FederationError> {
let schema_definition = self.make_mut(&mut schema.schema);
if schema_definition
.directives
.iter()
.any(|other_directive| other_directive.ptr_eq(&directive))
{
return Err(SingleFederationError::Internal {
message: format!(
"Directive application \"@{}\" already exists on schema definition",
directive.name,
),
}
.into());
}
let name = directive.name.clone();
schema_definition
.make_mut()
.directives
.insert(index, directive);
self.insert_directive_name_references(&mut schema.referencers, &name)?;
schema.links_metadata = links_metadata(&schema.schema)?.map(Box::new);
Ok(())
}
pub(crate) fn remove_directive_name(
&self,
schema: &mut FederationSchema,
name: &str,
) -> Result<(), FederationError> {
let is_link = Self::is_link(schema, name)?;
self.remove_directive_name_references(&mut schema.referencers, name);
self.make_mut(&mut schema.schema)
.make_mut()
.directives
.retain(|other_directive| other_directive.name != name);
if is_link {
schema.links_metadata = links_metadata(&schema.schema)?.map(Box::new);
}
Ok(())
}
fn insert_references(
&self,
schema_definition: &Node<SchemaDefinition>,
schema: &Schema,
referencers: &mut Referencers,
) -> Result<(), FederationError> {
validate_component_directives(schema_definition.directives.deref())?;
for directive_reference in schema_definition.directives.iter() {
self.insert_directive_name_references(referencers, &directive_reference.name)?;
}
for root_kind in SchemaRootDefinitionKind::iter() {
let child = SchemaRootDefinitionPosition { root_kind };
match root_kind {
SchemaRootDefinitionKind::Query => {
if let Some(root_type) = &schema_definition.query {
child.insert_references(root_type, schema, referencers)?;
}
}
SchemaRootDefinitionKind::Mutation => {
if let Some(root_type) = &schema_definition.mutation {
child.insert_references(root_type, schema, referencers)?;
}
}
SchemaRootDefinitionKind::Subscription => {
if let Some(root_type) = &schema_definition.subscription {
child.insert_references(root_type, schema, referencers)?;
}
}
}
}
Ok(())
}
fn insert_directive_name_references(
&self,
referencers: &mut Referencers,
name: &Name,
) -> Result<(), FederationError> {
let directive_referencers = referencers.directives.get_mut(name).ok_or_else(|| {
SingleFederationError::Internal {
message: format!(
"Schema definition's directive application \"@{name}\" does not refer to an existing directive.",
),
}
})?;
directive_referencers.schema = Some(SchemaDefinitionPosition);
Ok(())
}
fn remove_directive_name_references(&self, referencers: &mut Referencers, name: &str) {
let Some(directive_referencers) = referencers.directives.get_mut(name) else {
return;
};
directive_referencers.schema = None;
}
fn is_link(schema: &FederationSchema, name: &str) -> Result<bool, FederationError> {
Ok(match schema.metadata() {
Some(metadata) => {
let link_spec_definition = metadata.link_spec_definition()?;
let link_name_in_schema = link_spec_definition
.directive_name_in_schema(schema, &link_spec_definition.identity().name)?
.ok_or_else(|| SingleFederationError::Internal {
message: "Unexpectedly could not find core/link spec usage".to_owned(),
})?;
link_name_in_schema == name
}
None => false,
})
}
pub(crate) fn get_root_type<'schema>(
&self,
schema: &'schema FederationSchema,
kind: SchemaRootDefinitionKind,
) -> Option<&'schema ComponentName> {
let schema_definition = self.get(schema.schema());
match kind {
SchemaRootDefinitionKind::Query => schema_definition.query.as_ref(),
SchemaRootDefinitionKind::Mutation => schema_definition.mutation.as_ref(),
SchemaRootDefinitionKind::Subscription => schema_definition.subscription.as_ref(),
}
}
pub(crate) fn set_root_type(
&self,
schema: &mut FederationSchema,
kind: SchemaRootDefinitionKind,
type_name: ComponentName,
) -> Result<(), FederationError> {
let schema_definition = self.make_mut(&mut schema.schema);
match kind {
SchemaRootDefinitionKind::Query => {
schema_definition.make_mut().query = Some(type_name);
}
SchemaRootDefinitionKind::Mutation => {
schema_definition.make_mut().mutation = Some(type_name)
}
SchemaRootDefinitionKind::Subscription => {
schema_definition.make_mut().subscription = Some(type_name)
}
}
let schema_definition = self.get(&schema.schema);
self.insert_references(schema_definition, &schema.schema, &mut schema.referencers)?;
Ok(())
}
}
#[derive(Clone, PartialEq, Eq, Hash, derive_more::From, derive_more::Display)]
pub(crate) enum TagDirectiveTargetPosition {
ObjectField(ObjectFieldDefinitionPosition),
InterfaceField(InterfaceFieldDefinitionPosition),
UnionField(UnionTypenameFieldDefinitionPosition),
Object(ObjectTypeDefinitionPosition),
Interface(InterfaceTypeDefinitionPosition),
Union(UnionTypeDefinitionPosition),
ArgumentDefinition(FieldArgumentDefinitionPosition),
Scalar(ScalarTypeDefinitionPosition),
Enum(EnumTypeDefinitionPosition),
EnumValue(EnumValueDefinitionPosition),
InputObject(InputObjectTypeDefinitionPosition),
InputObjectFieldDefinition(InputObjectFieldDefinitionPosition),
Schema(SchemaDefinitionPosition),
DirectiveArgumentDefinition(DirectiveArgumentDefinitionPosition),
}
impl Debug for TagDirectiveTargetPosition {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
Self::ObjectField(p) => write!(f, "ObjectField({p})"),
Self::InterfaceField(p) => write!(f, "InterfaceField({p})"),
Self::UnionField(p) => write!(f, "UnionField({p})"),
Self::Object(p) => write!(f, "Object({p})"),
Self::Interface(p) => write!(f, "Interface({p})"),
Self::Union(p) => write!(f, "Union({p})"),
Self::ArgumentDefinition(p) => write!(f, "ArgumentDefinition({p})"),
Self::Scalar(p) => write!(f, "Scalar({p})"),
Self::Enum(p) => write!(f, "Enum({p})"),
Self::EnumValue(p) => write!(f, "EnumValue({p})"),
Self::InputObject(p) => write!(f, "InputObject({p})"),
Self::InputObjectFieldDefinition(p) => write!(f, "InputObjectFieldDefinition({p})"),
Self::Schema(p) => write!(f, "Schema({p})"),
Self::DirectiveArgumentDefinition(p) => {
write!(f, "DirectiveArgumentDefinition({p})")
}
}
}
}
fallible_conversions!(TagDirectiveTargetPosition::Object -> ObjectTypeDefinitionPosition);
fallible_conversions!(TagDirectiveTargetPosition::Interface -> InterfaceTypeDefinitionPosition);
fallible_conversions!(TagDirectiveTargetPosition::Union -> UnionTypeDefinitionPosition);
fallible_conversions!(TagDirectiveTargetPosition::Scalar -> ScalarTypeDefinitionPosition);
fallible_conversions!(TagDirectiveTargetPosition::Enum -> EnumTypeDefinitionPosition);
fallible_conversions!(TagDirectiveTargetPosition::InputObject -> InputObjectTypeDefinitionPosition);
impl TryFrom<TagDirectiveTargetPosition> for FieldDefinitionPosition {
type Error = &'static str;
fn try_from(dl: TagDirectiveTargetPosition) -> Result<Self, Self::Error> {
match dl {
TagDirectiveTargetPosition::ObjectField(field) => {
Ok(FieldDefinitionPosition::Object(field))
}
TagDirectiveTargetPosition::InterfaceField(field) => {
Ok(FieldDefinitionPosition::Interface(field))
}
TagDirectiveTargetPosition::UnionField(field) => {
Ok(FieldDefinitionPosition::Union(field))
}
_ => Err("No valid conversion"),
}
}
}
#[derive(
Debug,
Copy,
Clone,
PartialEq,
Eq,
Hash,
strum_macros::Display,
strum_macros::EnumIter,
Serialize,
)]
pub(crate) enum SchemaRootDefinitionKind {
#[strum(to_string = "query")]
Query,
#[strum(to_string = "mutation")]
Mutation,
#[strum(to_string = "subscription")]
Subscription,
}
impl From<SchemaRootDefinitionKind> for ast::OperationType {
fn from(value: SchemaRootDefinitionKind) -> Self {
match value {
SchemaRootDefinitionKind::Query => ast::OperationType::Query,
SchemaRootDefinitionKind::Mutation => ast::OperationType::Mutation,
SchemaRootDefinitionKind::Subscription => ast::OperationType::Subscription,
}
}
}
impl From<ast::OperationType> for SchemaRootDefinitionKind {
fn from(value: ast::OperationType) -> Self {
match value {
ast::OperationType::Query => SchemaRootDefinitionKind::Query,
ast::OperationType::Mutation => SchemaRootDefinitionKind::Mutation,
ast::OperationType::Subscription => SchemaRootDefinitionKind::Subscription,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub(crate) struct SchemaRootDefinitionPosition {
pub(crate) root_kind: SchemaRootDefinitionKind,
}
impl SchemaRootDefinitionPosition {
pub(crate) fn parent(&self) -> SchemaDefinitionPosition {
SchemaDefinitionPosition
}
pub(crate) fn get<'schema>(
&self,
schema: &'schema Schema,
) -> Result<&'schema ComponentName, FederationError> {
let schema_definition = self.parent().get(schema);
match self.root_kind {
SchemaRootDefinitionKind::Query => schema_definition.query.as_ref().ok_or_else(|| {
SingleFederationError::Internal {
message: format!("Schema definition has no root {self} type"),
}
.into()
}),
SchemaRootDefinitionKind::Mutation => {
schema_definition.mutation.as_ref().ok_or_else(|| {
SingleFederationError::Internal {
message: format!("Schema definition has no root {self} type"),
}
.into()
})
}
SchemaRootDefinitionKind::Subscription => {
schema_definition.subscription.as_ref().ok_or_else(|| {
SingleFederationError::Internal {
message: format!("Schema definition has no root {self} type"),
}
.into()
})
}
}
}
pub(crate) fn try_get<'schema>(
&self,
schema: &'schema Schema,
) -> Option<&'schema ComponentName> {
self.get(schema).ok()
}
pub(crate) fn insert(
&self,
schema: &mut FederationSchema,
root_type: ComponentName,
) -> Result<(), FederationError> {
if self.try_get(&schema.schema).is_some() {
return Err(SingleFederationError::Internal {
message: format!("Root {self} already exists on schema definition"),
}
.into());
}
let parent = self.parent().make_mut(&mut schema.schema).make_mut();
match self.root_kind {
SchemaRootDefinitionKind::Query => {
parent.query = Some(root_type);
}
SchemaRootDefinitionKind::Mutation => {
parent.mutation = Some(root_type);
}
SchemaRootDefinitionKind::Subscription => {
parent.subscription = Some(root_type);
}
}
self.insert_references(
self.get(&schema.schema)?,
&schema.schema,
&mut schema.referencers,
)
}
pub(crate) fn remove(&self, schema: &mut FederationSchema) -> Result<(), FederationError> {
let Some(root_type) = self.try_get(&schema.schema) else {
return Ok(());
};
self.remove_references(root_type, &schema.schema, &mut schema.referencers)?;
let parent = self.parent().make_mut(&mut schema.schema).make_mut();
match self.root_kind {
SchemaRootDefinitionKind::Query => {
parent.query = None;
}
SchemaRootDefinitionKind::Mutation => {
parent.mutation = None;
}
SchemaRootDefinitionKind::Subscription => {
parent.subscription = None;
}
}
Ok(())
}
fn insert_references(
&self,
root_type: &ComponentName,
schema: &Schema,
referencers: &mut Referencers,
) -> Result<(), FederationError> {
let object_type_referencers = referencers
.object_types
.get_mut(root_type.deref())
.ok_or_else(|| SingleFederationError::Internal {
message: format!(
"Root {} type \"{}\" does not refer to an existing object type.",
self,
root_type.deref()
),
})?;
object_type_referencers.schema_roots.insert(self.clone());
if self.root_kind == SchemaRootDefinitionKind::Query {
ObjectTypeDefinitionPosition {
type_name: root_type.name.clone(),
}
.insert_root_query_references(schema, referencers)?;
}
Ok(())
}
fn remove_references(
&self,
root_type: &ComponentName,
schema: &Schema,
referencers: &mut Referencers,
) -> Result<(), FederationError> {
if self.root_kind == SchemaRootDefinitionKind::Query {
ObjectTypeDefinitionPosition {
type_name: root_type.name.clone(),
}
.remove_root_query_references(schema, referencers)?;
}
let Some(object_type_referencers) = referencers.object_types.get_mut(root_type.deref())
else {
return Ok(());
};
object_type_referencers.schema_roots.shift_remove(self);
Ok(())
}
fn rename_type(
&self,
schema: &mut FederationSchema,
new_name: Name,
) -> Result<(), FederationError> {
let parent = self.parent().make_mut(&mut schema.schema).make_mut();
match self.root_kind {
SchemaRootDefinitionKind::Query => {
if let Some(query) = &mut parent.query {
query.name = new_name;
}
}
SchemaRootDefinitionKind::Mutation => {
if let Some(mutation) = &mut parent.mutation {
mutation.name = new_name;
}
}
SchemaRootDefinitionKind::Subscription => {
if let Some(subscription) = &mut parent.subscription {
subscription.name = new_name;
}
}
}
Ok(())
}
}
impl Display for SchemaRootDefinitionPosition {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.root_kind)
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub(crate) struct ScalarTypeDefinitionPosition {
pub(crate) type_name: Name,
}
impl ScalarTypeDefinitionPosition {
const EXPECTED: &'static str = "a scalar type";
pub(crate) fn get<'schema>(
&self,
schema: &'schema Schema,
) -> Result<&'schema Node<ScalarType>, PositionLookupError> {
schema
.types
.get(&self.type_name)
.ok_or_else(|| PositionLookupError::TypeMissing(self.type_name.clone()))
.and_then(|type_| {
if let ExtendedType::Scalar(type_) = type_ {
Ok(type_)
} else {
Err(PositionLookupError::TypeWrongKind(
self.type_name.clone(),
Self::EXPECTED,
))
}
})
}
pub(crate) fn try_get<'schema>(
&self,
schema: &'schema Schema,
) -> Option<&'schema Node<ScalarType>> {
self.get(schema).ok()
}
fn make_mut<'schema>(
&self,
schema: &'schema mut Schema,
) -> Result<&'schema mut Node<ScalarType>, PositionLookupError> {
schema
.types
.get_mut(&self.type_name)
.ok_or_else(|| PositionLookupError::TypeMissing(self.type_name.clone()))
.and_then(|type_| {
if let ExtendedType::Scalar(type_) = type_ {
Ok(type_)
} else {
Err(PositionLookupError::TypeWrongKind(
self.type_name.clone(),
Self::EXPECTED,
))
}
})
}
fn try_make_mut<'schema>(
&self,
schema: &'schema mut Schema,
) -> Option<&'schema mut Node<ScalarType>> {
if self.try_get(schema).is_some() {
self.make_mut(schema).ok()
} else {
None
}
}
pub(crate) fn pre_insert(&self, schema: &mut FederationSchema) -> Result<(), FederationError> {
if schema.referencers.contains_type_name(&self.type_name) {
bail!(r#"Type "{self}" has already been pre-inserted"#);
}
schema
.referencers
.scalar_types
.insert(self.type_name.clone(), Default::default());
Ok(())
}
pub(crate) fn insert(
&self,
schema: &mut FederationSchema,
type_: Node<ScalarType>,
) -> Result<(), FederationError> {
if self.type_name != type_.name {
return Err(SingleFederationError::Internal {
message: format!(
"Scalar type \"{}\" given type named \"{}\"",
self, type_.name,
),
}
.into());
}
if !schema
.referencers
.scalar_types
.contains_key(&self.type_name)
{
bail!(r#"Type "{self}" has not been pre-inserted"#);
}
if schema.schema.types.contains_key(&self.type_name) {
bail!(r#"Type "{self}" already exists in schema"#);
}
schema
.schema
.types
.insert(self.type_name.clone(), ExtendedType::Scalar(type_));
self.insert_references(self.get(&schema.schema)?, &mut schema.referencers)
}
pub(crate) fn remove(
&self,
schema: &mut FederationSchema,
) -> Result<Option<ScalarTypeReferencers>, FederationError> {
let Some(referencers) = self.remove_internal(schema)? else {
return Ok(None);
};
for field in &referencers.object_fields {
field.remove(schema)?;
}
for argument in &referencers.object_field_arguments {
argument.remove(schema)?;
}
for field in &referencers.interface_fields {
field.remove(schema)?;
}
for argument in &referencers.interface_field_arguments {
argument.remove(schema)?;
}
for field in &referencers.input_object_fields {
field.remove(schema)?;
}
for argument in &referencers.directive_arguments {
argument.remove(schema)?;
}
Ok(Some(referencers))
}
fn remove_internal(
&self,
schema: &mut FederationSchema,
) -> Result<Option<ScalarTypeReferencers>, FederationError> {
let Some(type_) = self.try_get(&schema.schema) else {
return Ok(None);
};
self.remove_references(type_, &mut schema.referencers);
schema.schema.types.shift_remove(&self.type_name);
Ok(Some(
schema
.referencers
.scalar_types
.shift_remove(&self.type_name)
.ok_or_else(|| SingleFederationError::Internal {
message: format!("Schema missing referencers for type \"{self}\""),
})?,
))
}
pub(crate) fn insert_directive(
&self,
schema: &mut FederationSchema,
directive: Component<Directive>,
) -> Result<(), FederationError> {
let type_ = self.make_mut(&mut schema.schema)?;
if type_
.directives
.iter()
.any(|other_directive| other_directive.ptr_eq(&directive))
{
return Err(SingleFederationError::Internal {
message: format!(
"Directive application \"@{}\" already exists on scalar type \"{}\"",
directive.name, self,
),
}
.into());
}
let name = directive.name.clone();
type_.make_mut().directives.push(directive);
self.insert_directive_name_references(&mut schema.referencers, &name)
}
pub(crate) fn remove_directive_name(&self, schema: &mut FederationSchema, name: &str) {
let Some(type_) = self.try_make_mut(&mut schema.schema) else {
return;
};
self.remove_directive_name_references(&mut schema.referencers, name);
type_
.make_mut()
.directives
.retain(|other_directive| other_directive.name != name);
}
fn insert_references(
&self,
type_: &Node<ScalarType>,
referencers: &mut Referencers,
) -> Result<(), FederationError> {
validate_component_directives(type_.directives.deref())?;
for directive_reference in type_.directives.iter() {
self.insert_directive_name_references(referencers, &directive_reference.name)?;
}
Ok(())
}
fn remove_references(&self, type_: &Node<ScalarType>, referencers: &mut Referencers) {
for directive_reference in type_.directives.iter() {
self.remove_directive_name_references(referencers, &directive_reference.name);
}
}
fn insert_directive_name_references(
&self,
referencers: &mut Referencers,
name: &Name,
) -> Result<(), FederationError> {
let directive_referencers = referencers.directives.get_mut(name).ok_or_else(|| {
SingleFederationError::Internal {
message: format!(
"Scalar type \"{self}\"'s directive application \"@{name}\" does not refer to an existing directive.",
),
}
})?;
directive_referencers.scalar_types.insert(self.clone());
Ok(())
}
fn remove_directive_name_references(&self, referencers: &mut Referencers, name: &str) {
let Some(directive_referencers) = referencers.directives.get_mut(name) else {
return;
};
directive_referencers.scalar_types.shift_remove(self);
}
fn rename(&self, schema: &mut FederationSchema, new_name: Name) -> Result<(), FederationError> {
self.make_mut(&mut schema.schema)?.make_mut().name = new_name.clone();
if let Some(scalar_type_referencers) =
schema.referencers.scalar_types.swap_remove(&self.type_name)
{
for pos in scalar_type_referencers.object_fields.iter() {
pos.rename_type(schema, new_name.clone())?;
}
for pos in scalar_type_referencers.object_field_arguments.iter() {
pos.rename_type(schema, new_name.clone())?;
}
for pos in scalar_type_referencers.interface_fields.iter() {
pos.rename_type(schema, new_name.clone())?;
}
for pos in scalar_type_referencers.interface_field_arguments.iter() {
pos.rename_type(schema, new_name.clone())?;
}
for pos in scalar_type_referencers.input_object_fields.iter() {
pos.rename_type(schema, new_name.clone())?;
}
for pos in scalar_type_referencers.directive_arguments.iter() {
pos.rename_type(schema, new_name.clone())?;
}
schema
.referencers
.scalar_types
.insert(new_name, scalar_type_referencers);
}
Ok(())
}
fn remove_extensions(&self, schema: &mut FederationSchema) -> Result<(), FederationError> {
for directive in self
.make_mut(&mut schema.schema)?
.make_mut()
.directives
.iter_mut()
{
directive.origin = ComponentOrigin::Definition;
}
Ok(())
}
pub(crate) fn has_applied_directive(
&self,
schema: &FederationSchema,
directive_name: &Name,
) -> bool {
if let Some(type_) = self.try_get(schema.schema()) {
return type_
.directives
.iter()
.any(|directive| &directive.name == directive_name);
}
false
}
pub(crate) fn get_all_applied_directives<'schema>(
&self,
schema: &'schema FederationSchema,
) -> Result<impl Iterator<Item = &'schema Component<Directive>>, FederationError> {
Ok(self.get(&schema.schema)?.directives.iter())
}
pub(crate) fn remove_directive(
&self,
schema: &mut FederationSchema,
directive: &Component<Directive>,
) {
let Some(obj) = self.try_make_mut(&mut schema.schema) else {
return;
};
if !obj.directives.iter().any(|other_directive| {
(other_directive.name == directive.name) && !other_directive.ptr_eq(directive)
}) {
self.remove_directive_name_references(&mut schema.referencers, &directive.name);
}
obj.make_mut()
.directives
.retain(|other_directive| !other_directive.ptr_eq(directive));
}
}
impl Display for ScalarTypeDefinitionPosition {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.type_name)
}
}
#[derive(Clone, PartialEq, Eq, Hash, Serialize)]
pub(crate) struct ObjectTypeDefinitionPosition {
pub(crate) type_name: Name,
}
impl ObjectTypeDefinitionPosition {
const EXPECTED: &'static str = "an object type";
pub(crate) fn new(type_name: Name) -> Self {
Self { type_name }
}
pub(crate) fn field(&self, field_name: Name) -> ObjectFieldDefinitionPosition {
ObjectFieldDefinitionPosition {
type_name: self.type_name.clone(),
field_name,
}
}
pub(crate) fn introspection_typename_field(&self) -> ObjectFieldDefinitionPosition {
self.field(INTROSPECTION_TYPENAME_FIELD_NAME.clone())
}
pub(crate) fn introspection_schema_field(&self) -> ObjectFieldDefinitionPosition {
self.field(name!("__schema"))
}
pub(crate) fn introspection_type_field(&self) -> ObjectFieldDefinitionPosition {
self.field(name!("__type"))
}
pub(crate) fn fields<'a>(
&'a self,
schema: &'a Schema,
) -> Result<impl Iterator<Item = ObjectFieldDefinitionPosition>, FederationError> {
Ok(self
.get(schema)?
.fields
.keys()
.map(|name| self.field(name.clone())))
}
pub(crate) fn get<'schema>(
&self,
schema: &'schema Schema,
) -> Result<&'schema Node<ObjectType>, PositionLookupError> {
schema
.types
.get(&self.type_name)
.ok_or_else(|| PositionLookupError::TypeMissing(self.type_name.clone()))
.and_then(|type_| {
if let ExtendedType::Object(type_) = type_ {
Ok(type_)
} else {
Err(PositionLookupError::TypeWrongKind(
self.type_name.clone(),
Self::EXPECTED,
))
}
})
}
pub(crate) fn try_get<'schema>(
&self,
schema: &'schema Schema,
) -> Option<&'schema Node<ObjectType>> {
self.get(schema).ok()
}
pub(crate) fn make_mut<'schema>(
&self,
schema: &'schema mut Schema,
) -> Result<&'schema mut Node<ObjectType>, PositionLookupError> {
schema
.types
.get_mut(&self.type_name)
.ok_or_else(|| PositionLookupError::TypeMissing(self.type_name.clone()))
.and_then(|type_| {
if let ExtendedType::Object(type_) = type_ {
Ok(type_)
} else {
Err(PositionLookupError::TypeWrongKind(
self.type_name.clone(),
Self::EXPECTED,
))
}
})
}
fn try_make_mut<'schema>(
&self,
schema: &'schema mut Schema,
) -> Option<&'schema mut Node<ObjectType>> {
if self.try_get(schema).is_some() {
self.make_mut(schema).ok()
} else {
None
}
}
pub(crate) fn pre_insert(&self, schema: &mut FederationSchema) -> Result<(), FederationError> {
if schema.referencers.contains_type_name(&self.type_name) {
bail!(r#"Type "{self}" has already been pre-inserted"#);
}
schema
.referencers
.object_types
.insert(self.type_name.clone(), Default::default());
Ok(())
}
pub(crate) fn insert(
&self,
schema: &mut FederationSchema,
type_: Node<ObjectType>,
) -> Result<(), FederationError> {
if self.type_name != type_.name {
return Err(SingleFederationError::Internal {
message: format!(
"Object type \"{}\" given type named \"{}\"",
self, type_.name,
),
}
.into());
}
if !schema
.referencers
.object_types
.contains_key(&self.type_name)
{
bail!(r#"Type "{self}" has not been pre-inserted"#);
}
if schema.schema.types.contains_key(&self.type_name) {
bail!(r#"Type "{self}" already exists in schema"#);
}
schema
.schema
.types
.insert(self.type_name.clone(), ExtendedType::Object(type_));
self.insert_references(
self.get(&schema.schema)?,
&schema.schema,
&mut schema.referencers,
)
}
pub(crate) fn remove(
&self,
schema: &mut FederationSchema,
) -> Result<Option<ObjectTypeReferencers>, FederationError> {
let Some(referencers) = self.remove_internal(schema)? else {
return Ok(None);
};
for root in &referencers.schema_roots {
root.remove(schema)?;
}
for field in &referencers.object_fields {
field.remove(schema)?;
}
for field in &referencers.interface_fields {
field.remove(schema)?;
}
for type_ in &referencers.union_types {
type_.remove_member(schema, &self.type_name);
}
Ok(Some(referencers))
}
pub(crate) fn remove_recursive(
&self,
schema: &mut FederationSchema,
) -> Result<(), FederationError> {
let Some(referencers) = self.remove_internal(schema)? else {
return Ok(());
};
for root in referencers.schema_roots {
root.remove(schema)?;
}
for field in referencers.object_fields {
field.remove_recursive(schema)?;
}
for field in referencers.interface_fields {
field.remove_recursive(schema)?;
}
for type_ in referencers.union_types {
type_.remove_member_recursive(schema, &self.type_name)?;
}
Ok(())
}
fn remove_internal(
&self,
schema: &mut FederationSchema,
) -> Result<Option<ObjectTypeReferencers>, FederationError> {
let Some(type_) = self.try_get(&schema.schema) else {
return Ok(None);
};
self.remove_references(type_, &schema.schema, &mut schema.referencers)?;
schema.schema.types.shift_remove(&self.type_name);
Ok(Some(
schema
.referencers
.object_types
.shift_remove(&self.type_name)
.ok_or_else(|| SingleFederationError::Internal {
message: format!("Schema missing referencers for type \"{self}\""),
})?,
))
}
pub(crate) fn insert_directive(
&self,
schema: &mut FederationSchema,
directive: Component<Directive>,
) -> Result<(), FederationError> {
let type_ = self.make_mut(&mut schema.schema)?;
if type_
.directives
.iter()
.any(|other_directive| other_directive.ptr_eq(&directive))
{
return Err(SingleFederationError::Internal {
message: format!(
"Directive application \"@{}\" already exists on object type \"{}\"",
directive.name, self,
),
}
.into());
}
let name = directive.name.clone();
type_.make_mut().directives.push(directive);
self.insert_directive_name_references(&mut schema.referencers, &name)
}
pub(crate) fn remove_directive_name(&self, schema: &mut FederationSchema, name: &str) {
let Some(type_) = self.try_make_mut(&mut schema.schema) else {
return;
};
self.remove_directive_name_references(&mut schema.referencers, name);
type_
.make_mut()
.directives
.retain(|other_directive| other_directive.name != name);
}
pub(crate) fn insert_implements_interface(
&self,
schema: &mut FederationSchema,
name: ComponentName,
) -> Result<(), FederationError> {
let type_ = self.make_mut(&mut schema.schema)?;
type_.make_mut().implements_interfaces.insert(name.clone());
self.insert_implements_interface_references(&mut schema.referencers, &name)
}
pub(crate) fn remove_implements_interface(&self, schema: &mut FederationSchema, name: &str) {
let Some(type_) = self.try_make_mut(&mut schema.schema) else {
return;
};
self.remove_implements_interface_references(&mut schema.referencers, name);
type_
.make_mut()
.implements_interfaces
.retain(|other_type| other_type != name);
}
fn insert_references(
&self,
type_: &Node<ObjectType>,
schema: &Schema,
referencers: &mut Referencers,
) -> Result<(), FederationError> {
validate_component_directives(type_.directives.deref())?;
for directive_reference in type_.directives.iter() {
self.insert_directive_name_references(referencers, &directive_reference.name)?;
}
for interface_type_reference in type_.implements_interfaces.iter() {
self.insert_implements_interface_references(
referencers,
interface_type_reference.deref(),
)?;
}
let introspection_typename_field = self.introspection_typename_field();
introspection_typename_field.insert_references(
introspection_typename_field.get(schema)?,
referencers,
true,
)?;
if let Some(root_query_type) = (SchemaRootDefinitionPosition {
root_kind: SchemaRootDefinitionKind::Query,
})
.try_get(schema)
{
if self.type_name == root_query_type.name {
self.insert_root_query_references(schema, referencers)?;
}
}
for (field_name, field) in type_.fields.iter() {
self.field(field_name.clone())
.insert_references(field, referencers, false)?;
}
Ok(())
}
fn remove_references(
&self,
type_: &Node<ObjectType>,
schema: &Schema,
referencers: &mut Referencers,
) -> Result<(), FederationError> {
for directive_reference in type_.directives.iter() {
self.remove_directive_name_references(referencers, &directive_reference.name);
}
for interface_type_reference in type_.implements_interfaces.iter() {
self.remove_implements_interface_references(
referencers,
interface_type_reference.deref(),
);
}
let introspection_typename_field = self.introspection_typename_field();
introspection_typename_field.remove_references(
introspection_typename_field.get(schema)?,
referencers,
true,
)?;
if let Some(root_query_type) = (SchemaRootDefinitionPosition {
root_kind: SchemaRootDefinitionKind::Query,
})
.try_get(schema)
{
if self.type_name == root_query_type.name {
self.remove_root_query_references(schema, referencers)?;
}
}
for (field_name, field) in type_.fields.iter() {
self.field(field_name.clone())
.remove_references(field, referencers, false)?;
}
Ok(())
}
fn insert_directive_name_references(
&self,
referencers: &mut Referencers,
name: &Name,
) -> Result<(), FederationError> {
let directive_referencers = referencers.directives.get_mut(name).ok_or_else(|| {
SingleFederationError::Internal {
message: format!(
"Object type \"{self}\"'s directive application \"@{name}\" does not refer to an existing directive.",
),
}
})?;
directive_referencers.object_types.insert(self.clone());
Ok(())
}
fn remove_directive_name_references(&self, referencers: &mut Referencers, name: &str) {
let Some(directive_referencers) = referencers.directives.get_mut(name) else {
return;
};
directive_referencers.object_types.shift_remove(self);
}
fn insert_implements_interface_references(
&self,
referencers: &mut Referencers,
name: &Name,
) -> Result<(), FederationError> {
let interface_type_referencers = referencers.interface_types.get_mut(name).ok_or_else(|| {
SingleFederationError::Internal {
message: format!(
"Object type \"{self}\"'s implements \"{name}\" does not refer to an existing interface.",
),
}
})?;
interface_type_referencers.object_types.insert(self.clone());
Ok(())
}
fn remove_implements_interface_references(&self, referencers: &mut Referencers, name: &str) {
let Some(interface_type_referencers) = referencers.interface_types.get_mut(name) else {
return;
};
interface_type_referencers.object_types.shift_remove(self);
}
fn insert_root_query_references(
&self,
schema: &Schema,
referencers: &mut Referencers,
) -> Result<(), FederationError> {
let introspection_schema_field = self.introspection_schema_field();
if let Some(field) = introspection_schema_field.try_get(schema) {
introspection_schema_field.insert_references(field, referencers, true)?;
}
let introspection_type_field = self.introspection_type_field();
if let Some(field) = introspection_type_field.try_get(schema) {
introspection_type_field.insert_references(field, referencers, true)?;
}
Ok(())
}
fn remove_root_query_references(
&self,
schema: &Schema,
referencers: &mut Referencers,
) -> Result<(), FederationError> {
let introspection_schema_field = self.introspection_schema_field();
if let Some(field) = introspection_schema_field.try_get(schema) {
introspection_schema_field.remove_references(field, referencers, true)?;
}
let introspection_type_field = self.introspection_type_field();
if let Some(field) = introspection_type_field.try_get(schema) {
introspection_type_field.remove_references(field, referencers, true)?;
}
Ok(())
}
fn rename(&self, schema: &mut FederationSchema, new_name: Name) -> Result<(), FederationError> {
self.make_mut(&mut schema.schema)?.make_mut().name = new_name.clone();
if let Some(object_type_referencers) =
schema.referencers.object_types.swap_remove(&self.type_name)
{
for pos in object_type_referencers.schema_roots.iter() {
pos.rename_type(schema, new_name.clone())?;
}
for pos in object_type_referencers.object_fields.iter() {
pos.rename_type(schema, new_name.clone())?;
}
for pos in object_type_referencers.interface_fields.iter() {
pos.rename_type(schema, new_name.clone())?;
}
for pos in object_type_referencers.union_types.iter() {
pos.rename_member(schema, &self.type_name, new_name.clone())?;
}
schema
.referencers
.object_types
.insert(new_name.clone(), object_type_referencers);
}
Ok(())
}
fn rename_implemented_interface(
&self,
schema: &mut FederationSchema,
old_name: &Name,
new_name: Name,
) -> Result<(), FederationError> {
let type_ = self.make_mut(&mut schema.schema)?.make_mut();
type_.implements_interfaces.swap_remove(old_name);
type_.implements_interfaces.insert(new_name.into());
Ok(())
}
fn remove_extensions(&self, schema: &mut FederationSchema) -> Result<(), FederationError> {
let type_ = self.make_mut(&mut schema.schema)?.make_mut();
for directive in type_.directives.iter_mut() {
directive.origin = ComponentOrigin::Definition;
}
type_.implements_interfaces = type_
.implements_interfaces
.iter()
.map(|i| {
let mut i = i.clone();
i.origin = ComponentOrigin::Definition;
i
})
.collect();
for (_, field) in type_.fields.iter_mut() {
field.origin = ComponentOrigin::Definition;
}
Ok(())
}
pub(crate) fn has_applied_directive(
&self,
schema: &FederationSchema,
directive_name: &Name,
) -> bool {
if let Some(type_) = self.try_get(schema.schema()) {
return type_
.directives
.iter()
.any(|directive| &directive.name == directive_name);
}
false
}
pub(crate) fn get_all_applied_directives<'schema>(
&self,
schema: &'schema FederationSchema,
) -> Result<impl Iterator<Item = &'schema Component<Directive>>, FederationError> {
Ok(self.get(&schema.schema)?.directives.iter())
}
pub(crate) fn remove_directive(
&self,
schema: &mut FederationSchema,
directive: &Component<Directive>,
) {
let Some(obj) = self.try_make_mut(&mut schema.schema) else {
return;
};
if !obj.directives.iter().any(|other_directive| {
(other_directive.name == directive.name) && !other_directive.ptr_eq(directive)
}) {
self.remove_directive_name_references(&mut schema.referencers, &directive.name);
}
obj.make_mut()
.directives
.retain(|other_directive| !other_directive.ptr_eq(directive));
}
}
impl Display for ObjectTypeDefinitionPosition {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.type_name)
}
}
impl Debug for ObjectTypeDefinitionPosition {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "Object({self})")
}
}
#[derive(Clone, PartialEq, Eq, Hash, derive_more::From, derive_more::Display)]
pub(crate) enum FieldArgumentDefinitionPosition {
Interface(InterfaceFieldArgumentDefinitionPosition),
Object(ObjectFieldArgumentDefinitionPosition),
}
impl FieldArgumentDefinitionPosition {
pub(crate) fn get<'schema>(
&self,
schema: &'schema Schema,
) -> Result<&'schema Node<InputValueDefinition>, PositionLookupError> {
match self {
Self::Interface(p) => p.get(schema),
Self::Object(p) => p.get(schema),
}
}
pub(crate) fn make_mut<'schema>(
&self,
schema: &'schema mut Schema,
) -> Result<&'schema mut Node<InputValueDefinition>, PositionLookupError> {
match self {
Self::Interface(p) => p.make_mut(schema),
Self::Object(p) => p.make_mut(schema),
}
}
}
impl Debug for FieldArgumentDefinitionPosition {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
Self::Interface(p) => write!(f, "Interface({p})"),
Self::Object(p) => write!(f, "Object({p})"),
}
}
}
impl HasDefaultValue for FieldArgumentDefinitionPosition {
fn is_input_field() -> bool {
false
}
fn get_default_value<'schema>(
&self,
schema: &'schema FederationSchema,
) -> Option<&'schema Node<ast::Value>> {
match self {
Self::Interface(pos) => pos.get_default_value(schema),
Self::Object(pos) => pos.get_default_value(schema),
}
}
fn set_default_value(
&self,
schema: &mut FederationSchema,
default: Option<Node<ast::Value>>,
) -> Result<(), FederationError> {
match self {
Self::Interface(pos) => pos.set_default_value(schema, default),
Self::Object(pos) => pos.set_default_value(schema, default),
}
}
}
#[derive(Clone, PartialEq, Eq, Hash, Serialize)]
pub(crate) struct ObjectFieldDefinitionPosition {
pub(crate) type_name: Name,
pub(crate) field_name: Name,
}
impl ObjectFieldDefinitionPosition {
pub(crate) fn parent(&self) -> ObjectTypeDefinitionPosition {
ObjectTypeDefinitionPosition {
type_name: self.type_name.clone(),
}
}
pub(crate) fn argument(&self, argument_name: Name) -> ObjectFieldArgumentDefinitionPosition {
ObjectFieldArgumentDefinitionPosition {
type_name: self.type_name.clone(),
field_name: self.field_name.clone(),
argument_name,
}
}
pub(crate) fn get<'schema>(
&self,
schema: &'schema Schema,
) -> Result<&'schema Component<FieldDefinition>, PositionLookupError> {
let parent = self.parent();
parent.get(schema)?;
schema
.type_field(&self.type_name, &self.field_name)
.map_err(|_| {
PositionLookupError::MissingField(
"Object",
self.type_name.clone(),
self.field_name.clone(),
)
})
}
pub(crate) fn try_get<'schema>(
&self,
schema: &'schema Schema,
) -> Option<&'schema Component<FieldDefinition>> {
self.get(schema).ok()
}
pub(crate) fn make_mut<'schema>(
&self,
schema: &'schema mut Schema,
) -> Result<&'schema mut Component<FieldDefinition>, PositionLookupError> {
let parent = self.parent();
let type_ = parent.make_mut(schema)?.make_mut();
if is_graphql_reserved_name(&self.field_name) {
return Err(PositionLookupError::MutateReservedField(
"object field",
self.type_name.clone(),
self.field_name.clone(),
));
}
type_.fields.get_mut(&self.field_name).ok_or_else(|| {
PositionLookupError::MissingField(
"Object",
self.type_name.clone(),
self.field_name.clone(),
)
})
}
fn try_make_mut<'schema>(
&self,
schema: &'schema mut Schema,
) -> Option<&'schema mut Component<FieldDefinition>> {
if self.try_get(schema).is_some() {
self.make_mut(schema).ok()
} else {
None
}
}
pub(crate) fn insert(
&self,
schema: &mut FederationSchema,
field: Component<FieldDefinition>,
) -> Result<(), FederationError> {
if self.field_name != field.name {
return Err(SingleFederationError::Internal {
message: format!(
"Object field \"{}\" given field named \"{}\"",
self, field.name,
),
}
.into());
}
if self.try_get(&schema.schema).is_some() {
bail!(r#"Object field "{self}" already exists in schema"#);
}
self.parent()
.make_mut(&mut schema.schema)?
.make_mut()
.fields
.insert(self.field_name.clone(), field);
self.insert_references(self.get(&schema.schema)?, &mut schema.referencers, false)?;
Ok(())
}
pub(crate) fn remove(&self, schema: &mut FederationSchema) -> Result<(), FederationError> {
let Some(field) = self.try_get(&schema.schema) else {
return Ok(());
};
self.remove_references(field, &mut schema.referencers, false)?;
self.parent()
.make_mut(&mut schema.schema)?
.make_mut()
.fields
.shift_remove(&self.field_name);
Ok(())
}
pub(crate) fn remove_recursive(
&self,
schema: &mut FederationSchema,
) -> Result<(), FederationError> {
self.remove(schema)?;
let parent = self.parent();
let Some(type_) = parent.try_get(&schema.schema) else {
return Ok(());
};
if type_.fields.is_empty() {
parent.remove_recursive(schema)?;
}
Ok(())
}
pub(crate) fn insert_directive(
&self,
schema: &mut FederationSchema,
directive: Node<Directive>,
) -> Result<(), FederationError> {
let field = self.make_mut(&mut schema.schema)?;
if field
.directives
.iter()
.any(|other_directive| other_directive.ptr_eq(&directive))
{
return Err(SingleFederationError::Internal {
message: format!(
"Directive application \"@{}\" already exists on object field \"{}\"",
directive.name, self,
),
}
.into());
}
let name = directive.name.clone();
field.make_mut().directives.push(directive);
self.insert_directive_name_references(&mut schema.referencers, &name)
}
pub(crate) fn get_all_applied_directives<'schema>(
&self,
schema: &'schema FederationSchema,
) -> Result<impl Iterator<Item = &'schema Node<Directive>>, FederationError> {
Ok(self.get(&schema.schema)?.directives.iter())
}
pub(crate) fn remove_directive_name(&self, schema: &mut FederationSchema, name: &str) {
let Some(field) = self.try_make_mut(&mut schema.schema) else {
return;
};
self.remove_directive_name_references(&mut schema.referencers, name);
field
.make_mut()
.directives
.retain(|other_directive| other_directive.name != name);
}
pub(crate) fn remove_directive(
&self,
schema: &mut FederationSchema,
directive: &Node<Directive>,
) {
let Some(field) = self.try_make_mut(&mut schema.schema) else {
return;
};
if !field.directives.iter().any(|other_directive| {
(other_directive.name == directive.name) && !other_directive.ptr_eq(directive)
}) {
self.remove_directive_name_references(&mut schema.referencers, &directive.name);
}
field
.make_mut()
.directives
.retain(|other_directive| !other_directive.ptr_eq(directive));
}
fn insert_references(
&self,
field: &Component<FieldDefinition>,
referencers: &mut Referencers,
allow_built_ins: bool,
) -> Result<(), FederationError> {
if !allow_built_ins && is_graphql_reserved_name(&self.field_name) {
bail!(r#"Cannot insert reserved object field "{self}""#);
}
validate_node_directives(field.directives.deref())?;
for directive_reference in field.directives.iter() {
self.insert_directive_name_references(referencers, &directive_reference.name)?;
}
self.insert_type_references(field, referencers)?;
validate_arguments(&field.arguments)?;
for argument in field.arguments.iter() {
self.argument(argument.name.clone())
.insert_references(argument, referencers)?;
}
Ok(())
}
fn remove_references(
&self,
field: &Component<FieldDefinition>,
referencers: &mut Referencers,
allow_built_ins: bool,
) -> Result<(), FederationError> {
if !allow_built_ins && is_graphql_reserved_name(&self.field_name) {
bail!(r#"Cannot remove reserved object field "{self}""#);
}
for directive_reference in field.directives.iter() {
self.remove_directive_name_references(referencers, &directive_reference.name);
}
self.remove_type_references(field, referencers);
for argument in field.arguments.iter() {
self.argument(argument.name.clone())
.remove_references(argument, referencers)?;
}
Ok(())
}
fn insert_directive_name_references(
&self,
referencers: &mut Referencers,
name: &Name,
) -> Result<(), FederationError> {
let directive_referencers = referencers.directives.get_mut(name).ok_or_else(|| {
SingleFederationError::Internal {
message: format!(
"Object field \"{self}\"'s directive application \"@{name}\" does not refer to an existing directive.",
),
}
})?;
directive_referencers.object_fields.insert(self.clone());
Ok(())
}
fn remove_directive_name_references(&self, referencers: &mut Referencers, name: &str) {
let Some(directive_referencers) = referencers.directives.get_mut(name) else {
return;
};
directive_referencers.object_fields.shift_remove(self);
}
fn insert_type_references(
&self,
field: &Component<FieldDefinition>,
referencers: &mut Referencers,
) -> Result<(), FederationError> {
let output_type_reference = field.ty.inner_named_type();
if let Some(scalar_type_referencers) =
referencers.scalar_types.get_mut(output_type_reference)
{
scalar_type_referencers.object_fields.insert(self.clone());
} else if let Some(object_type_referencers) =
referencers.object_types.get_mut(output_type_reference)
{
object_type_referencers.object_fields.insert(self.clone());
} else if let Some(interface_type_referencers) =
referencers.interface_types.get_mut(output_type_reference)
{
interface_type_referencers
.object_fields
.insert(self.clone());
} else if let Some(union_type_referencers) =
referencers.union_types.get_mut(output_type_reference)
{
union_type_referencers.object_fields.insert(self.clone());
} else if let Some(enum_type_referencers) =
referencers.enum_types.get_mut(output_type_reference)
{
enum_type_referencers.object_fields.insert(self.clone());
} else {
return Err(FederationError::internal(format!(
"Object field \"{}\"'s inner type \"{}\" does not refer to an existing output type.",
self,
output_type_reference.deref(),
)));
}
Ok(())
}
fn remove_type_references(
&self,
field: &Component<FieldDefinition>,
referencers: &mut Referencers,
) {
let output_type_reference = field.ty.inner_named_type();
if let Some(scalar_type_referencers) =
referencers.scalar_types.get_mut(output_type_reference)
{
scalar_type_referencers.object_fields.shift_remove(self);
} else if let Some(object_type_referencers) =
referencers.object_types.get_mut(output_type_reference)
{
object_type_referencers.object_fields.shift_remove(self);
} else if let Some(interface_type_referencers) =
referencers.interface_types.get_mut(output_type_reference)
{
interface_type_referencers.object_fields.shift_remove(self);
} else if let Some(union_type_referencers) =
referencers.union_types.get_mut(output_type_reference)
{
union_type_referencers.object_fields.shift_remove(self);
} else if let Some(enum_type_referencers) =
referencers.enum_types.get_mut(output_type_reference)
{
enum_type_referencers.object_fields.shift_remove(self);
}
}
fn rename_type(
&self,
schema: &mut FederationSchema,
new_name: Name,
) -> Result<(), FederationError> {
let field = self.make_mut(&mut schema.schema)?.make_mut();
rename_type(&mut field.ty, new_name);
Ok(())
}
}
impl Display for ObjectFieldDefinitionPosition {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "{}.{}", self.type_name, self.field_name)
}
}
impl Debug for ObjectFieldDefinitionPosition {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "ObjectField({self})")
}
}
impl HasArguments for ObjectFieldDefinitionPosition {
type ArgumentPosition = ObjectFieldArgumentDefinitionPosition;
fn argument_position(&self, name: Name) -> Self::ArgumentPosition {
self.argument(name)
}
fn get_argument<'schema>(
&self,
schema: &'schema FederationSchema,
name: &Name,
) -> Option<&'schema Node<InputValueDefinition>> {
self.argument_position(name.clone()).try_get(&schema.schema)
}
fn get_arguments<'schema>(
&self,
schema: &'schema FederationSchema,
) -> Result<&'schema Vec<Node<InputValueDefinition>>, FederationError> {
Ok(&self.get(schema.schema())?.arguments)
}
fn insert_argument(
&self,
schema: &mut FederationSchema,
arg: Node<InputValueDefinition>,
) -> Result<(), FederationError> {
self.argument_position(arg.name.clone()).insert(schema, arg)
}
fn remove_argument(
&self,
schema: &mut FederationSchema,
name: &Name,
) -> Result<(), FederationError> {
self.argument_position(name.clone()).remove(schema)
}
}
#[derive(Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub(crate) struct ObjectFieldArgumentDefinitionPosition {
pub(crate) type_name: Name,
pub(crate) field_name: Name,
pub(crate) argument_name: Name,
}
impl ObjectFieldArgumentDefinitionPosition {
pub(crate) fn get_all_applied_directives<'schema>(
&self,
schema: &'schema FederationSchema,
) -> Result<impl Iterator<Item = &'schema Node<Directive>>, FederationError> {
Ok(self.get(&schema.schema)?.directives.iter())
}
pub(crate) fn parent(&self) -> ObjectFieldDefinitionPosition {
ObjectFieldDefinitionPosition {
type_name: self.type_name.clone(),
field_name: self.field_name.clone(),
}
}
pub(crate) fn get<'schema>(
&self,
schema: &'schema Schema,
) -> Result<&'schema Node<InputValueDefinition>, PositionLookupError> {
let field = self.parent().get(schema)?;
field.argument_by_name(&self.argument_name).ok_or_else(|| {
PositionLookupError::MissingFieldArgument(
"Object field",
self.type_name.clone(),
self.field_name.clone(),
self.argument_name.clone(),
)
})
}
pub(crate) fn try_get<'schema>(
&self,
schema: &'schema Schema,
) -> Option<&'schema Node<InputValueDefinition>> {
self.get(schema).ok()
}
fn make_mut<'schema>(
&self,
schema: &'schema mut Schema,
) -> Result<&'schema mut Node<InputValueDefinition>, PositionLookupError> {
let parent = self.parent();
let type_ = parent.make_mut(schema)?.make_mut();
type_
.arguments
.iter_mut()
.find(|a| a.name == self.argument_name)
.ok_or_else(|| {
PositionLookupError::MissingFieldArgument(
"Object field",
self.type_name.clone(),
self.field_name.clone(),
self.argument_name.clone(),
)
})
}
fn try_make_mut<'schema>(
&self,
schema: &'schema mut Schema,
) -> Option<&'schema mut Node<InputValueDefinition>> {
if self.try_get(schema).is_some() {
self.make_mut(schema).ok()
} else {
None
}
}
pub(crate) fn remove(&self, schema: &mut FederationSchema) -> Result<(), FederationError> {
let Some(argument) = self.try_get(&schema.schema) else {
return Ok(());
};
self.remove_references(argument, &mut schema.referencers)?;
self.parent()
.make_mut(&mut schema.schema)?
.make_mut()
.arguments
.retain(|other_argument| other_argument.name != self.argument_name);
Ok(())
}
pub(crate) fn insert_directive(
&self,
schema: &mut FederationSchema,
directive: Node<Directive>,
) -> Result<(), FederationError> {
let argument = self.make_mut(&mut schema.schema)?;
if argument
.directives
.iter()
.any(|other_directive| other_directive.ptr_eq(&directive))
{
return Err(SingleFederationError::Internal {
message: format!(
"Directive application \"@{}\" already exists on object field argument \"{}\"",
directive.name, self,
),
}
.into());
}
let name = directive.name.clone();
argument.make_mut().directives.push(directive);
self.insert_directive_name_references(&mut schema.referencers, &name)
}
pub(crate) fn remove_directive_name(&self, schema: &mut FederationSchema, name: &str) {
let Some(argument) = self.try_make_mut(&mut schema.schema) else {
return;
};
self.remove_directive_name_references(&mut schema.referencers, name);
argument
.make_mut()
.directives
.retain(|other_directive| other_directive.name != name);
}
fn insert(
&self,
schema: &mut FederationSchema,
argument: Node<InputValueDefinition>,
) -> Result<(), FederationError> {
let parent = self.parent().make_mut(&mut schema.schema)?;
if parent.argument_by_name(&self.argument_name).is_some() {
bail!(r#"Object field argument "{self}" already exists in schema"#);
}
self.insert_references(&argument, &mut schema.referencers)?;
parent.make_mut().arguments.push(argument);
Ok(())
}
fn insert_references(
&self,
argument: &Node<InputValueDefinition>,
referencers: &mut Referencers,
) -> Result<(), FederationError> {
if is_graphql_reserved_name(&self.argument_name) {
bail!(r#"Cannot insert reserved object field argument "{self}""#);
}
validate_node_directives(argument.directives.deref())?;
for directive_reference in argument.directives.iter() {
self.insert_directive_name_references(referencers, &directive_reference.name)?;
}
self.insert_type_references(argument, referencers)
}
fn remove_references(
&self,
argument: &Node<InputValueDefinition>,
referencers: &mut Referencers,
) -> Result<(), FederationError> {
if is_graphql_reserved_name(&self.argument_name) {
bail!(r#"Cannot remove reserved object field argument "{self}""#);
}
for directive_reference in argument.directives.iter() {
self.remove_directive_name_references(referencers, &directive_reference.name);
}
self.remove_type_references(argument, referencers);
Ok(())
}
fn insert_directive_name_references(
&self,
referencers: &mut Referencers,
name: &Name,
) -> Result<(), FederationError> {
let directive_referencers = referencers.directives.get_mut(name).ok_or_else(|| {
SingleFederationError::Internal {
message: format!(
"Object field argument \"{self}\"'s directive application \"@{name}\" does not refer to an existing directive.",
),
}
})?;
directive_referencers
.object_field_arguments
.insert(self.clone());
Ok(())
}
fn remove_directive_name_references(&self, referencers: &mut Referencers, name: &str) {
let Some(directive_referencers) = referencers.directives.get_mut(name) else {
return;
};
directive_referencers
.object_field_arguments
.shift_remove(self);
}
fn insert_type_references(
&self,
argument: &Node<InputValueDefinition>,
referencers: &mut Referencers,
) -> Result<(), FederationError> {
let input_type_reference = argument.ty.inner_named_type();
if let Some(scalar_type_referencers) =
referencers.scalar_types.get_mut(input_type_reference)
{
scalar_type_referencers
.object_field_arguments
.insert(self.clone());
} else if let Some(enum_type_referencers) =
referencers.enum_types.get_mut(input_type_reference)
{
enum_type_referencers
.object_field_arguments
.insert(self.clone());
} else if let Some(input_object_type_referencers) =
referencers.input_object_types.get_mut(input_type_reference)
{
input_object_type_referencers
.object_field_arguments
.insert(self.clone());
} else {
return Err(FederationError::internal(format!(
"Object field argument \"{}\"'s inner type \"{}\" does not refer to an existing input type.",
self,
input_type_reference.deref(),
)));
}
Ok(())
}
fn remove_type_references(
&self,
argument: &Node<InputValueDefinition>,
referencers: &mut Referencers,
) {
let input_type_reference = argument.ty.inner_named_type();
if let Some(scalar_type_referencers) =
referencers.scalar_types.get_mut(input_type_reference)
{
scalar_type_referencers
.object_field_arguments
.shift_remove(self);
} else if let Some(enum_type_referencers) =
referencers.enum_types.get_mut(input_type_reference)
{
enum_type_referencers
.object_field_arguments
.shift_remove(self);
} else if let Some(input_object_type_referencers) =
referencers.input_object_types.get_mut(input_type_reference)
{
input_object_type_referencers
.object_field_arguments
.shift_remove(self);
}
}
fn rename_type(
&self,
schema: &mut FederationSchema,
new_name: Name,
) -> Result<(), FederationError> {
let field = self.make_mut(&mut schema.schema)?.make_mut();
rename_type(field.ty.make_mut(), new_name);
Ok(())
}
}
impl Display for ObjectFieldArgumentDefinitionPosition {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(
f,
"{}.{}({}:)",
self.type_name, self.field_name, self.argument_name
)
}
}
impl Debug for ObjectFieldArgumentDefinitionPosition {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "ObjectFieldArgument({self})")
}
}
impl HasDefaultValue for ObjectFieldArgumentDefinitionPosition {
fn is_input_field() -> bool {
false
}
fn get_default_value<'schema>(
&self,
schema: &'schema FederationSchema,
) -> Option<&'schema Node<ast::Value>> {
self.try_get(&schema.schema)
.and_then(|arg| arg.default_value.as_ref())
}
fn set_default_value(
&self,
schema: &mut FederationSchema,
default: Option<Node<ast::Value>>,
) -> Result<(), FederationError> {
let arg = self.make_mut(&mut schema.schema)?.make_mut();
arg.default_value = default;
Ok(())
}
}
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
pub(crate) struct ObjectOrInterfaceFieldDirectivePosition {
pub(crate) field: ObjectOrInterfaceFieldDefinitionPosition,
pub(crate) directive_name: Name,
pub(crate) directive_index: usize,
}
impl ObjectOrInterfaceFieldDirectivePosition {
pub(crate) fn add_argument(
&self,
schema: &mut FederationSchema,
argument: Node<Argument>,
) -> Result<(), FederationError> {
let directive = match self.field {
ObjectOrInterfaceFieldDefinitionPosition::Object(ref field) => {
let field = field.make_mut(&mut schema.schema)?;
field
.make_mut()
.directives
.get_mut(self.directive_index)
.ok_or_else(|| SingleFederationError::Internal {
message: format!(
"Object field \"{}\"'s directive application at index {} does not exist",
self.field, self.directive_index,
),
})?
}
ObjectOrInterfaceFieldDefinitionPosition::Interface(ref field) => {
let field = field.make_mut(&mut schema.schema)?;
field
.make_mut()
.directives
.get_mut(self.directive_index)
.ok_or_else(|| SingleFederationError::Internal {
message: format!(
"Interface field \"{}\"'s directive application at index {} does not exist",
self.field, self.directive_index,
),
})?
}
};
directive.make_mut().arguments.push(argument);
Ok(())
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize)]
pub(crate) struct InterfaceTypeDefinitionPosition {
pub(crate) type_name: Name,
}
impl InterfaceTypeDefinitionPosition {
const EXPECTED: &'static str = "an interface type";
pub(crate) fn new(type_name: Name) -> Self {
Self { type_name }
}
pub(crate) fn field(&self, field_name: Name) -> InterfaceFieldDefinitionPosition {
InterfaceFieldDefinitionPosition {
type_name: self.type_name.clone(),
field_name,
}
}
pub(crate) fn introspection_typename_field(&self) -> InterfaceFieldDefinitionPosition {
self.field(INTROSPECTION_TYPENAME_FIELD_NAME.clone())
}
pub(crate) fn fields<'a>(
&'a self,
schema: &'a Schema,
) -> Result<impl Iterator<Item = InterfaceFieldDefinitionPosition>, FederationError> {
Ok(self
.get(schema)?
.fields
.keys()
.map(|name| self.field(name.clone())))
}
pub(crate) fn get<'schema>(
&self,
schema: &'schema Schema,
) -> Result<&'schema Node<InterfaceType>, PositionLookupError> {
schema
.types
.get(&self.type_name)
.ok_or_else(|| PositionLookupError::TypeMissing(self.type_name.clone()))
.and_then(|type_| {
if let ExtendedType::Interface(type_) = type_ {
Ok(type_)
} else {
Err(PositionLookupError::TypeWrongKind(
self.type_name.clone(),
Self::EXPECTED,
))
}
})
}
pub(crate) fn try_get<'schema>(
&self,
schema: &'schema Schema,
) -> Option<&'schema Node<InterfaceType>> {
self.get(schema).ok()
}
fn make_mut<'schema>(
&self,
schema: &'schema mut Schema,
) -> Result<&'schema mut Node<InterfaceType>, PositionLookupError> {
schema
.types
.get_mut(&self.type_name)
.ok_or_else(|| PositionLookupError::TypeMissing(self.type_name.clone()))
.and_then(|type_| {
if let ExtendedType::Interface(type_) = type_ {
Ok(type_)
} else {
Err(PositionLookupError::TypeWrongKind(
self.type_name.clone(),
Self::EXPECTED,
))
}
})
}
fn try_make_mut<'schema>(
&self,
schema: &'schema mut Schema,
) -> Option<&'schema mut Node<InterfaceType>> {
if self.try_get(schema).is_some() {
self.make_mut(schema).ok()
} else {
None
}
}
pub(crate) fn pre_insert(&self, schema: &mut FederationSchema) -> Result<(), FederationError> {
if schema.referencers.contains_type_name(&self.type_name) {
bail!(r#"Type "{self}" has already been pre-inserted"#);
}
schema
.referencers
.interface_types
.insert(self.type_name.clone(), Default::default());
Ok(())
}
pub(crate) fn insert(
&self,
schema: &mut FederationSchema,
type_: Node<InterfaceType>,
) -> Result<(), FederationError> {
if self.type_name != type_.name {
return Err(SingleFederationError::Internal {
message: format!(
"Interface type \"{}\" given type named \"{}\"",
self, type_.name,
),
}
.into());
}
if !schema
.referencers
.interface_types
.contains_key(&self.type_name)
{
bail!(r#"Type "{self}" has not been pre-inserted"#);
}
if schema.schema.types.contains_key(&self.type_name) {
bail!(r#"Type "{self}" already exists in schema"#);
}
schema
.schema
.types
.insert(self.type_name.clone(), ExtendedType::Interface(type_));
self.insert_references(
self.get(&schema.schema)?,
&schema.schema,
&mut schema.referencers,
)
}
pub(crate) fn insert_empty(
&self,
schema: &mut FederationSchema,
) -> Result<(), FederationError> {
self.insert(
schema,
Node::new(InterfaceType {
description: None,
name: self.type_name.clone(),
implements_interfaces: Default::default(),
fields: Default::default(),
directives: Default::default(),
}),
)
}
pub(crate) fn remove(
&self,
schema: &mut FederationSchema,
) -> Result<Option<InterfaceTypeReferencers>, FederationError> {
let Some(referencers) = self.remove_internal(schema)? else {
return Ok(None);
};
for type_ in &referencers.object_types {
type_.remove_implements_interface(schema, &self.type_name);
}
for field in &referencers.object_fields {
field.remove(schema)?;
}
for type_ in &referencers.interface_types {
type_.remove_implements_interface(schema, &self.type_name);
}
for field in &referencers.interface_fields {
field.remove(schema)?;
}
Ok(Some(referencers))
}
pub(crate) fn remove_recursive(
&self,
schema: &mut FederationSchema,
) -> Result<(), FederationError> {
let Some(referencers) = self.remove_internal(schema)? else {
return Ok(());
};
for type_ in referencers.object_types {
type_.remove_implements_interface(schema, &self.type_name);
}
for field in referencers.object_fields {
field.remove_recursive(schema)?;
}
for type_ in referencers.interface_types {
type_.remove_implements_interface(schema, &self.type_name);
}
for field in referencers.interface_fields {
field.remove_recursive(schema)?;
}
Ok(())
}
fn remove_internal(
&self,
schema: &mut FederationSchema,
) -> Result<Option<InterfaceTypeReferencers>, FederationError> {
let Some(type_) = self.try_get(&schema.schema) else {
return Ok(None);
};
self.remove_references(type_, &schema.schema, &mut schema.referencers)?;
schema.schema.types.shift_remove(&self.type_name);
Ok(Some(
schema
.referencers
.interface_types
.shift_remove(&self.type_name)
.ok_or_else(|| SingleFederationError::Internal {
message: format!("Schema missing referencers for type \"{self}\""),
})?,
))
}
pub(crate) fn insert_directive(
&self,
schema: &mut FederationSchema,
directive: Component<Directive>,
) -> Result<(), FederationError> {
let type_ = self.make_mut(&mut schema.schema)?;
if type_
.directives
.iter()
.any(|other_directive| other_directive.ptr_eq(&directive))
{
return Err(SingleFederationError::Internal {
message: format!(
"Directive application \"@{}\" already exists on interface type \"{}\"",
directive.name, self,
),
}
.into());
}
let name = directive.name.clone();
type_.make_mut().directives.push(directive);
self.insert_directive_name_references(&mut schema.referencers, &name)
}
pub(crate) fn remove_directive_name(&self, schema: &mut FederationSchema, name: &str) {
let Some(type_) = self.try_make_mut(&mut schema.schema) else {
return;
};
self.remove_directive_name_references(&mut schema.referencers, name);
type_
.make_mut()
.directives
.retain(|other_directive| other_directive.name != name);
}
pub(crate) fn insert_implements_interface(
&self,
schema: &mut FederationSchema,
name: ComponentName,
) -> Result<(), FederationError> {
let type_ = self.make_mut(&mut schema.schema)?;
type_.make_mut().implements_interfaces.insert(name.clone());
self.insert_implements_interface_references(&mut schema.referencers, &name)
}
pub(crate) fn remove_implements_interface(&self, schema: &mut FederationSchema, name: &str) {
let Some(type_) = self.try_make_mut(&mut schema.schema) else {
return;
};
self.remove_implements_interface_references(&mut schema.referencers, name);
type_
.make_mut()
.implements_interfaces
.retain(|other_type| other_type != name);
}
fn insert_references(
&self,
type_: &Node<InterfaceType>,
schema: &Schema,
referencers: &mut Referencers,
) -> Result<(), FederationError> {
validate_component_directives(type_.directives.deref())?;
for directive_reference in type_.directives.iter() {
self.insert_directive_name_references(referencers, &directive_reference.name)?;
}
for interface_type_reference in type_.implements_interfaces.iter() {
self.insert_implements_interface_references(
referencers,
interface_type_reference.deref(),
)?;
}
let introspection_typename_field = self.introspection_typename_field();
introspection_typename_field.insert_references(
introspection_typename_field.get(schema)?,
referencers,
true,
)?;
for (field_name, field) in type_.fields.iter() {
self.field(field_name.clone())
.insert_references(field, referencers, false)?;
}
Ok(())
}
fn remove_references(
&self,
type_: &Node<InterfaceType>,
schema: &Schema,
referencers: &mut Referencers,
) -> Result<(), FederationError> {
for directive_reference in type_.directives.iter() {
self.remove_directive_name_references(referencers, &directive_reference.name);
}
for interface_type_reference in type_.implements_interfaces.iter() {
self.remove_implements_interface_references(
referencers,
interface_type_reference.deref(),
);
}
let introspection_typename_field = self.introspection_typename_field();
introspection_typename_field.remove_references(
introspection_typename_field.get(schema)?,
referencers,
true,
)?;
for (field_name, field) in type_.fields.iter() {
self.field(field_name.clone())
.remove_references(field, referencers, false)?;
}
Ok(())
}
fn insert_directive_name_references(
&self,
referencers: &mut Referencers,
name: &Name,
) -> Result<(), FederationError> {
let directive_referencers = referencers.directives.get_mut(name).ok_or_else(|| {
SingleFederationError::Internal {
message: format!(
"Interface type \"{self}\"'s directive application \"@{name}\" does not refer to an existing directive.",
),
}
})?;
directive_referencers.interface_types.insert(self.clone());
Ok(())
}
fn remove_directive_name_references(&self, referencers: &mut Referencers, name: &str) {
let Some(directive_referencers) = referencers.directives.get_mut(name) else {
return;
};
directive_referencers.interface_types.shift_remove(self);
}
fn insert_implements_interface_references(
&self,
referencers: &mut Referencers,
name: &Name,
) -> Result<(), FederationError> {
let interface_type_referencers = referencers.interface_types.get_mut(name).ok_or_else(|| {
SingleFederationError::Internal {
message: format!(
"Interface type \"{self}\"'s implements \"{name}\" does not refer to an existing interface.",
),
}
})?;
interface_type_referencers
.interface_types
.insert(self.clone());
Ok(())
}
fn remove_implements_interface_references(&self, referencers: &mut Referencers, name: &str) {
let Some(interface_type_referencers) = referencers.interface_types.get_mut(name) else {
return;
};
interface_type_referencers
.interface_types
.shift_remove(self);
}
fn rename(&self, schema: &mut FederationSchema, new_name: Name) -> Result<(), FederationError> {
self.make_mut(&mut schema.schema)?.make_mut().name = new_name.clone();
if let Some(interface_type_referencers) = schema
.referencers
.interface_types
.swap_remove(&self.type_name)
{
for pos in interface_type_referencers.object_types.iter() {
pos.rename_implemented_interface(schema, &self.type_name, new_name.clone())?;
}
for pos in interface_type_referencers.object_fields.iter() {
pos.rename_type(schema, new_name.clone())?;
}
for pos in interface_type_referencers.interface_types.iter() {
pos.rename_implemented_interface(schema, &self.type_name, new_name.clone())?;
}
for pos in interface_type_referencers.interface_fields.iter() {
pos.rename_type(schema, new_name.clone())?;
}
schema
.referencers
.interface_types
.insert(new_name.clone(), interface_type_referencers);
}
Ok(())
}
fn rename_implemented_interface(
&self,
schema: &mut FederationSchema,
old_name: &Name,
new_name: Name,
) -> Result<(), FederationError> {
let type_ = self.make_mut(&mut schema.schema)?.make_mut();
type_.implements_interfaces.swap_remove(old_name);
type_.implements_interfaces.insert(new_name.into());
Ok(())
}
fn remove_extensions(&self, schema: &mut FederationSchema) -> Result<(), FederationError> {
let type_ = self.make_mut(&mut schema.schema)?.make_mut();
for directive in type_.directives.iter_mut() {
directive.origin = ComponentOrigin::Definition;
}
type_.implements_interfaces = type_
.implements_interfaces
.iter()
.map(|i| {
let mut i = i.clone();
i.origin = ComponentOrigin::Definition;
i
})
.collect();
for (_, field) in type_.fields.iter_mut() {
field.origin = ComponentOrigin::Definition;
}
Ok(())
}
pub(crate) fn has_applied_directive(
&self,
schema: &FederationSchema,
directive_name: &Name,
) -> bool {
if let Some(type_) = self.try_get(schema.schema()) {
return type_
.directives
.iter()
.any(|directive| &directive.name == directive_name);
}
false
}
pub(crate) fn get_all_applied_directives<'schema>(
&self,
schema: &'schema FederationSchema,
) -> Result<impl Iterator<Item = &'schema Component<Directive>>, FederationError> {
Ok(self.get(&schema.schema)?.directives.iter())
}
pub(crate) fn remove_directive(
&self,
schema: &mut FederationSchema,
directive: &Component<Directive>,
) {
let Some(obj) = self.try_make_mut(&mut schema.schema) else {
return;
};
if !obj.directives.iter().any(|other_directive| {
(other_directive.name == directive.name) && !other_directive.ptr_eq(directive)
}) {
self.remove_directive_name_references(&mut schema.referencers, &directive.name);
}
obj.make_mut()
.directives
.retain(|other_directive| !other_directive.ptr_eq(directive));
}
pub(crate) fn implementers<'a>(
&'a self,
schema: &'a FederationSchema,
) -> Result<impl Iterator<Item = ObjectOrInterfaceTypeDefinitionPosition> + 'a, FederationError>
{
let referencers = schema
.referencers()
.interface_types
.get(&self.type_name)
.ok_or_else(|| {
FederationError::internal(format!(
"Schema missing referencers for interface type \"{}\"",
self.type_name
))
})?;
Ok(referencers
.object_types
.iter()
.cloned()
.map(|obj| obj.into())
.chain(
referencers
.interface_types
.iter()
.cloned()
.map(|interface| interface.into()),
))
}
}
impl Display for InterfaceTypeDefinitionPosition {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.type_name)
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize)]
pub(crate) struct InterfaceFieldDefinitionPosition {
pub(crate) type_name: Name,
pub(crate) field_name: Name,
}
impl InterfaceFieldDefinitionPosition {
pub(crate) fn parent(&self) -> InterfaceTypeDefinitionPosition {
InterfaceTypeDefinitionPosition {
type_name: self.type_name.clone(),
}
}
pub(crate) fn argument(&self, argument_name: Name) -> InterfaceFieldArgumentDefinitionPosition {
InterfaceFieldArgumentDefinitionPosition {
type_name: self.type_name.clone(),
field_name: self.field_name.clone(),
argument_name,
}
}
pub(crate) fn get<'schema>(
&self,
schema: &'schema Schema,
) -> Result<&'schema Component<FieldDefinition>, PositionLookupError> {
let parent = self.parent();
parent.get(schema)?;
schema
.type_field(&self.type_name, &self.field_name)
.map_err(|_| {
PositionLookupError::MissingField(
"Interface",
self.type_name.clone(),
self.field_name.clone(),
)
})
}
pub(crate) fn try_get<'schema>(
&self,
schema: &'schema Schema,
) -> Option<&'schema Component<FieldDefinition>> {
self.get(schema).ok()
}
pub(crate) fn make_mut<'schema>(
&self,
schema: &'schema mut Schema,
) -> Result<&'schema mut Component<FieldDefinition>, PositionLookupError> {
let parent = self.parent();
let type_ = parent.make_mut(schema)?.make_mut();
if is_graphql_reserved_name(&self.field_name) {
return Err(PositionLookupError::MutateReservedField(
"interface field",
self.type_name.clone(),
self.field_name.clone(),
));
}
type_.fields.get_mut(&self.field_name).ok_or_else(|| {
PositionLookupError::MissingField(
"Interface",
self.type_name.clone(),
self.field_name.clone(),
)
})
}
fn try_make_mut<'schema>(
&self,
schema: &'schema mut Schema,
) -> Option<&'schema mut Component<FieldDefinition>> {
if self.try_get(schema).is_some() {
self.make_mut(schema).ok()
} else {
None
}
}
pub(crate) fn insert(
&self,
schema: &mut FederationSchema,
field: Component<FieldDefinition>,
) -> Result<(), FederationError> {
if self.field_name != field.name {
return Err(SingleFederationError::Internal {
message: format!(
"Interface field \"{}\" given field named \"{}\"",
self, field.name,
),
}
.into());
}
if self.try_get(&schema.schema).is_some() {
bail!(r#"Interface field "{self}" already exists in schema"#);
}
self.parent()
.make_mut(&mut schema.schema)?
.make_mut()
.fields
.insert(self.field_name.clone(), field);
self.insert_references(self.get(&schema.schema)?, &mut schema.referencers, false)
}
pub(crate) fn remove(&self, schema: &mut FederationSchema) -> Result<(), FederationError> {
let Some(field) = self.try_get(&schema.schema) else {
return Ok(());
};
self.remove_references(field, &mut schema.referencers, false)?;
self.parent()
.make_mut(&mut schema.schema)?
.make_mut()
.fields
.shift_remove(&self.field_name);
Ok(())
}
pub(crate) fn remove_recursive(
&self,
schema: &mut FederationSchema,
) -> Result<(), FederationError> {
self.remove(schema)?;
let parent = self.parent();
let Some(type_) = parent.try_get(&schema.schema) else {
return Ok(());
};
if type_.fields.is_empty() {
parent.remove_recursive(schema)?;
}
Ok(())
}
pub(crate) fn insert_directive(
&self,
schema: &mut FederationSchema,
directive: Node<Directive>,
) -> Result<(), FederationError> {
let field = self.make_mut(&mut schema.schema)?;
if field
.directives
.iter()
.any(|other_directive| other_directive.ptr_eq(&directive))
{
return Err(SingleFederationError::Internal {
message: format!(
"Directive application \"@{}\" already exists on interface field \"{}\"",
directive.name, self,
),
}
.into());
}
let name = directive.name.clone();
field.make_mut().directives.push(directive);
self.insert_directive_name_references(&mut schema.referencers, &name)
}
pub(crate) fn get_all_applied_directives<'schema>(
&self,
schema: &'schema FederationSchema,
) -> Result<impl Iterator<Item = &'schema Node<Directive>>, FederationError> {
Ok(self.get(&schema.schema)?.directives.iter())
}
pub(crate) fn remove_directive_name(&self, schema: &mut FederationSchema, name: &str) {
let Some(field) = self.try_make_mut(&mut schema.schema) else {
return;
};
self.remove_directive_name_references(&mut schema.referencers, name);
field
.make_mut()
.directives
.retain(|other_directive| other_directive.name != name);
}
pub(crate) fn remove_directive(
&self,
schema: &mut FederationSchema,
directive: &Node<Directive>,
) {
let Some(field) = self.try_make_mut(&mut schema.schema) else {
return;
};
if !field.directives.iter().any(|other_directive| {
(other_directive.name == directive.name) && !other_directive.ptr_eq(directive)
}) {
self.remove_directive_name_references(&mut schema.referencers, &directive.name);
}
field
.make_mut()
.directives
.retain(|other_directive| !other_directive.ptr_eq(directive));
}
fn insert_references(
&self,
field: &Component<FieldDefinition>,
referencers: &mut Referencers,
allow_built_ins: bool,
) -> Result<(), FederationError> {
if !allow_built_ins && is_graphql_reserved_name(&self.field_name) {
bail!(r#"Cannot insert reserved interface field "{self}""#);
}
validate_node_directives(field.directives.deref())?;
for directive_reference in field.directives.iter() {
self.insert_directive_name_references(referencers, &directive_reference.name)?;
}
self.insert_type_references(field, referencers)?;
validate_arguments(&field.arguments)?;
for argument in field.arguments.iter() {
self.argument(argument.name.clone())
.insert_references(argument, referencers)?;
}
Ok(())
}
fn remove_references(
&self,
field: &Component<FieldDefinition>,
referencers: &mut Referencers,
allow_built_ins: bool,
) -> Result<(), FederationError> {
if !allow_built_ins && is_graphql_reserved_name(&self.field_name) {
bail!(r#"Cannot remove reserved interface field "{self}""#);
}
for directive_reference in field.directives.iter() {
self.remove_directive_name_references(referencers, &directive_reference.name);
}
self.remove_type_references(field, referencers);
for argument in field.arguments.iter() {
self.argument(argument.name.clone())
.remove_references(argument, referencers)?;
}
Ok(())
}
fn insert_directive_name_references(
&self,
referencers: &mut Referencers,
name: &Name,
) -> Result<(), FederationError> {
let directive_referencers = referencers.directives.get_mut(name).ok_or_else(|| {
SingleFederationError::Internal {
message: format!(
"Interface field \"{self}\"'s directive application \"@{name}\" does not refer to an existing directive.",
),
}
})?;
directive_referencers.interface_fields.insert(self.clone());
Ok(())
}
fn remove_directive_name_references(&self, referencers: &mut Referencers, name: &str) {
let Some(directive_referencers) = referencers.directives.get_mut(name) else {
return;
};
directive_referencers.interface_fields.shift_remove(self);
}
fn insert_type_references(
&self,
field: &Component<FieldDefinition>,
referencers: &mut Referencers,
) -> Result<(), FederationError> {
let output_type_reference = field.ty.inner_named_type();
if let Some(scalar_type_referencers) =
referencers.scalar_types.get_mut(output_type_reference)
{
scalar_type_referencers
.interface_fields
.insert(self.clone());
} else if let Some(object_type_referencers) =
referencers.object_types.get_mut(output_type_reference)
{
object_type_referencers
.interface_fields
.insert(self.clone());
} else if let Some(interface_type_referencers) =
referencers.interface_types.get_mut(output_type_reference)
{
interface_type_referencers
.interface_fields
.insert(self.clone());
} else if let Some(union_type_referencers) =
referencers.union_types.get_mut(output_type_reference)
{
union_type_referencers.interface_fields.insert(self.clone());
} else if let Some(enum_type_referencers) =
referencers.enum_types.get_mut(output_type_reference)
{
enum_type_referencers.interface_fields.insert(self.clone());
} else {
return Err(FederationError::internal(format!(
"Interface field \"{}\"'s inner type \"{}\" does not refer to an existing output type.",
self,
output_type_reference.deref(),
)));
}
Ok(())
}
fn remove_type_references(
&self,
field: &Component<FieldDefinition>,
referencers: &mut Referencers,
) {
let output_type_reference = field.ty.inner_named_type();
if let Some(scalar_type_referencers) =
referencers.scalar_types.get_mut(output_type_reference)
{
scalar_type_referencers.interface_fields.shift_remove(self);
} else if let Some(object_type_referencers) =
referencers.object_types.get_mut(output_type_reference)
{
object_type_referencers.interface_fields.shift_remove(self);
} else if let Some(interface_type_referencers) =
referencers.interface_types.get_mut(output_type_reference)
{
interface_type_referencers
.interface_fields
.shift_remove(self);
} else if let Some(union_type_referencers) =
referencers.union_types.get_mut(output_type_reference)
{
union_type_referencers.interface_fields.shift_remove(self);
} else if let Some(enum_type_referencers) =
referencers.enum_types.get_mut(output_type_reference)
{
enum_type_referencers.interface_fields.shift_remove(self);
}
}
fn rename_type(
&self,
schema: &mut FederationSchema,
new_name: Name,
) -> Result<(), FederationError> {
let field = self.make_mut(&mut schema.schema)?.make_mut();
rename_type(&mut field.ty, new_name);
Ok(())
}
}
impl Display for InterfaceFieldDefinitionPosition {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "{}.{}", self.type_name, self.field_name)
}
}
impl HasArguments for InterfaceFieldDefinitionPosition {
type ArgumentPosition = InterfaceFieldArgumentDefinitionPosition;
fn argument_position(&self, name: Name) -> Self::ArgumentPosition {
self.argument(name)
}
fn get_argument<'schema>(
&self,
schema: &'schema FederationSchema,
name: &Name,
) -> Option<&'schema Node<InputValueDefinition>> {
self.argument_position(name.clone()).try_get(&schema.schema)
}
fn get_arguments<'schema>(
&self,
schema: &'schema FederationSchema,
) -> Result<&'schema Vec<Node<InputValueDefinition>>, FederationError> {
Ok(&self.get(&schema.schema)?.arguments)
}
fn insert_argument(
&self,
schema: &mut FederationSchema,
arg: Node<InputValueDefinition>,
) -> Result<(), FederationError> {
self.argument_position(arg.name.clone()).insert(schema, arg)
}
fn remove_argument(
&self,
schema: &mut FederationSchema,
name: &Name,
) -> Result<(), FederationError> {
self.argument_position(name.clone()).remove(schema)
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub(crate) struct InterfaceFieldArgumentDefinitionPosition {
pub(crate) type_name: Name,
pub(crate) field_name: Name,
pub(crate) argument_name: Name,
}
impl InterfaceFieldArgumentDefinitionPosition {
pub(crate) fn get_all_applied_directives<'schema>(
&self,
schema: &'schema FederationSchema,
) -> Result<impl Iterator<Item = &'schema Node<Directive>>, FederationError> {
Ok(self.get(&schema.schema)?.directives.iter())
}
pub(crate) fn parent(&self) -> InterfaceFieldDefinitionPosition {
InterfaceFieldDefinitionPosition {
type_name: self.type_name.clone(),
field_name: self.field_name.clone(),
}
}
pub(crate) fn get<'schema>(
&self,
schema: &'schema Schema,
) -> Result<&'schema Node<InputValueDefinition>, PositionLookupError> {
let field = self.parent().get(schema)?;
field.argument_by_name(&self.argument_name).ok_or_else(|| {
PositionLookupError::MissingFieldArgument(
"Interface field",
self.type_name.clone(),
self.field_name.clone(),
self.argument_name.clone(),
)
})
}
pub(crate) fn try_get<'schema>(
&self,
schema: &'schema Schema,
) -> Option<&'schema Node<InputValueDefinition>> {
self.get(schema).ok()
}
fn make_mut<'schema>(
&self,
schema: &'schema mut Schema,
) -> Result<&'schema mut Node<InputValueDefinition>, PositionLookupError> {
let parent = self.parent();
let type_ = parent.make_mut(schema)?.make_mut();
type_
.arguments
.iter_mut()
.find(|a| a.name == self.argument_name)
.ok_or_else(|| {
PositionLookupError::MissingFieldArgument(
"Interface field",
self.type_name.clone(),
self.field_name.clone(),
self.argument_name.clone(),
)
})
}
fn try_make_mut<'schema>(
&self,
schema: &'schema mut Schema,
) -> Option<&'schema mut Node<InputValueDefinition>> {
if self.try_get(schema).is_some() {
self.make_mut(schema).ok()
} else {
None
}
}
pub(crate) fn remove(&self, schema: &mut FederationSchema) -> Result<(), FederationError> {
let Some(argument) = self.try_get(&schema.schema) else {
return Ok(());
};
self.remove_references(argument, &mut schema.referencers)?;
self.parent()
.make_mut(&mut schema.schema)?
.make_mut()
.arguments
.retain(|other_argument| other_argument.name != self.argument_name);
Ok(())
}
pub(crate) fn insert_directive(
&self,
schema: &mut FederationSchema,
directive: Node<Directive>,
) -> Result<(), FederationError> {
let argument = self.make_mut(&mut schema.schema)?;
if argument
.directives
.iter()
.any(|other_directive| other_directive.ptr_eq(&directive))
{
return Err(
SingleFederationError::Internal {
message: format!(
"Directive application \"@{}\" already exists on interface field argument \"{}\"",
directive.name,
self,
)
}.into()
);
}
let name = directive.name.clone();
argument.make_mut().directives.push(directive);
self.insert_directive_name_references(&mut schema.referencers, &name)
}
pub(crate) fn remove_directive_name(&self, schema: &mut FederationSchema, name: &str) {
let Some(argument) = self.try_make_mut(&mut schema.schema) else {
return;
};
self.remove_directive_name_references(&mut schema.referencers, name);
argument
.make_mut()
.directives
.retain(|other_directive| other_directive.name != name);
}
fn insert(
&self,
schema: &mut FederationSchema,
argument: Node<InputValueDefinition>,
) -> Result<(), FederationError> {
let parent = self.parent().make_mut(&mut schema.schema)?;
if parent.argument_by_name(&argument.name).is_some() {
bail!(r#"Interface field argument "{self}" already exists in schema"#);
}
self.insert_references(&argument, &mut schema.referencers)?;
parent.make_mut().arguments.push(argument);
Ok(())
}
fn insert_references(
&self,
argument: &Node<InputValueDefinition>,
referencers: &mut Referencers,
) -> Result<(), FederationError> {
if is_graphql_reserved_name(&self.argument_name) {
bail!(r#"Cannot insert reserved interface field argument "{self}""#);
}
validate_node_directives(argument.directives.deref())?;
for directive_reference in argument.directives.iter() {
self.insert_directive_name_references(referencers, &directive_reference.name)?;
}
self.insert_type_references(argument, referencers)
}
fn remove_references(
&self,
argument: &Node<InputValueDefinition>,
referencers: &mut Referencers,
) -> Result<(), FederationError> {
if is_graphql_reserved_name(&self.argument_name) {
bail!(r#"Cannot remove reserved interface field argument "{self}""#);
}
for directive_reference in argument.directives.iter() {
self.remove_directive_name_references(referencers, &directive_reference.name);
}
self.remove_type_references(argument, referencers);
Ok(())
}
fn insert_directive_name_references(
&self,
referencers: &mut Referencers,
name: &Name,
) -> Result<(), FederationError> {
let directive_referencers = referencers.directives.get_mut(name).ok_or_else(|| {
SingleFederationError::Internal {
message: format!(
"Interface field argument \"{self}\"'s directive application \"@{name}\" does not refer to an existing directive.",
),
}
})?;
directive_referencers
.interface_field_arguments
.insert(self.clone());
Ok(())
}
fn remove_directive_name_references(&self, referencers: &mut Referencers, name: &str) {
let Some(directive_referencers) = referencers.directives.get_mut(name) else {
return;
};
directive_referencers
.interface_field_arguments
.shift_remove(self);
}
fn insert_type_references(
&self,
argument: &Node<InputValueDefinition>,
referencers: &mut Referencers,
) -> Result<(), FederationError> {
let input_type_reference = argument.ty.inner_named_type();
if let Some(scalar_type_referencers) =
referencers.scalar_types.get_mut(input_type_reference)
{
scalar_type_referencers
.interface_field_arguments
.insert(self.clone());
} else if let Some(enum_type_referencers) =
referencers.enum_types.get_mut(input_type_reference)
{
enum_type_referencers
.interface_field_arguments
.insert(self.clone());
} else if let Some(input_object_type_referencers) =
referencers.input_object_types.get_mut(input_type_reference)
{
input_object_type_referencers
.interface_field_arguments
.insert(self.clone());
} else {
return Err(FederationError::internal(format!(
"Interface field argument \"{}\"'s inner type \"{}\" does not refer to an existing input type.",
self,
input_type_reference.deref(),
)));
}
Ok(())
}
fn remove_type_references(
&self,
argument: &Node<InputValueDefinition>,
referencers: &mut Referencers,
) {
let input_type_reference = argument.ty.inner_named_type();
if let Some(scalar_type_referencers) =
referencers.scalar_types.get_mut(input_type_reference)
{
scalar_type_referencers
.interface_field_arguments
.shift_remove(self);
} else if let Some(enum_type_referencers) =
referencers.enum_types.get_mut(input_type_reference)
{
enum_type_referencers
.interface_field_arguments
.shift_remove(self);
} else if let Some(input_object_type_referencers) =
referencers.input_object_types.get_mut(input_type_reference)
{
input_object_type_referencers
.interface_field_arguments
.shift_remove(self);
}
}
fn rename_type(
&self,
schema: &mut FederationSchema,
new_name: Name,
) -> Result<(), FederationError> {
let argument = self.make_mut(&mut schema.schema)?.make_mut();
rename_type(argument.ty.make_mut(), new_name);
Ok(())
}
}
impl Display for InterfaceFieldArgumentDefinitionPosition {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(
f,
"{}.{}({}:)",
self.type_name, self.field_name, self.argument_name
)
}
}
impl HasDefaultValue for InterfaceFieldArgumentDefinitionPosition {
fn is_input_field() -> bool {
false
}
fn get_default_value<'schema>(
&self,
schema: &'schema FederationSchema,
) -> Option<&'schema Node<ast::Value>> {
self.try_get(&schema.schema)
.and_then(|arg| arg.default_value.as_ref())
}
fn set_default_value(
&self,
schema: &mut FederationSchema,
default: Option<Node<ast::Value>>,
) -> Result<(), FederationError> {
let arg = self.make_mut(&mut schema.schema)?.make_mut();
arg.default_value = default;
Ok(())
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize)]
pub(crate) struct UnionTypeDefinitionPosition {
pub(crate) type_name: Name,
}
impl UnionTypeDefinitionPosition {
const EXPECTED: &'static str = "a union type";
pub(crate) fn introspection_typename_field(&self) -> UnionTypenameFieldDefinitionPosition {
UnionTypenameFieldDefinitionPosition {
type_name: self.type_name.clone(),
}
}
pub(crate) fn get<'schema>(
&self,
schema: &'schema Schema,
) -> Result<&'schema Node<UnionType>, PositionLookupError> {
schema
.types
.get(&self.type_name)
.ok_or_else(|| PositionLookupError::TypeMissing(self.type_name.clone()))
.and_then(|type_| {
if let ExtendedType::Union(type_) = type_ {
Ok(type_)
} else {
Err(PositionLookupError::TypeWrongKind(
self.type_name.clone(),
Self::EXPECTED,
))
}
})
}
pub(crate) fn try_get<'schema>(
&self,
schema: &'schema Schema,
) -> Option<&'schema Node<UnionType>> {
self.get(schema).ok()
}
fn make_mut<'schema>(
&self,
schema: &'schema mut Schema,
) -> Result<&'schema mut Node<UnionType>, PositionLookupError> {
schema
.types
.get_mut(&self.type_name)
.ok_or_else(|| PositionLookupError::TypeMissing(self.type_name.clone()))
.and_then(|type_| {
if let ExtendedType::Union(type_) = type_ {
Ok(type_)
} else {
Err(PositionLookupError::TypeWrongKind(
self.type_name.clone(),
Self::EXPECTED,
))
}
})
}
fn try_make_mut<'schema>(
&self,
schema: &'schema mut Schema,
) -> Option<&'schema mut Node<UnionType>> {
if self.try_get(schema).is_some() {
self.make_mut(schema).ok()
} else {
None
}
}
pub(crate) fn pre_insert(&self, schema: &mut FederationSchema) -> Result<(), FederationError> {
if schema.referencers.contains_type_name(&self.type_name) {
bail!(r#"Type "{self}" has already been pre-inserted"#);
}
schema
.referencers
.union_types
.insert(self.type_name.clone(), Default::default());
Ok(())
}
pub(crate) fn insert(
&self,
schema: &mut FederationSchema,
type_: Node<UnionType>,
) -> Result<(), FederationError> {
if self.type_name != type_.name {
return Err(SingleFederationError::Internal {
message: format!(
"Union type \"{}\" given type named \"{}\"",
self, type_.name,
),
}
.into());
}
if !schema.referencers.union_types.contains_key(&self.type_name) {
bail!(r#"Type "{self}" has not been pre-inserted"#);
}
if schema.schema.types.contains_key(&self.type_name) {
bail!(r#"Type "{self}" already exists in schema"#);
}
schema
.schema
.types
.insert(self.type_name.clone(), ExtendedType::Union(type_));
self.insert_references(self.get(&schema.schema)?, &mut schema.referencers)
}
pub(crate) fn remove(
&self,
schema: &mut FederationSchema,
) -> Result<Option<UnionTypeReferencers>, FederationError> {
let Some(referencers) = self.remove_internal(schema)? else {
return Ok(None);
};
for field in &referencers.object_fields {
field.remove(schema)?;
}
for field in &referencers.interface_fields {
field.remove(schema)?;
}
Ok(Some(referencers))
}
pub(crate) fn remove_recursive(
&self,
schema: &mut FederationSchema,
) -> Result<(), FederationError> {
let Some(referencers) = self.remove_internal(schema)? else {
return Ok(());
};
for field in referencers.object_fields {
field.remove_recursive(schema)?;
}
for field in referencers.interface_fields {
field.remove_recursive(schema)?;
}
Ok(())
}
fn remove_internal(
&self,
schema: &mut FederationSchema,
) -> Result<Option<UnionTypeReferencers>, FederationError> {
let Some(type_) = self.try_get(&schema.schema) else {
return Ok(None);
};
self.remove_references(type_, &mut schema.referencers);
schema.schema.types.shift_remove(&self.type_name);
Ok(Some(
schema
.referencers
.union_types
.shift_remove(&self.type_name)
.ok_or_else(|| SingleFederationError::Internal {
message: format!("Schema missing referencers for type \"{self}\""),
})?,
))
}
pub(crate) fn insert_directive(
&self,
schema: &mut FederationSchema,
directive: Component<Directive>,
) -> Result<(), FederationError> {
let type_ = self.make_mut(&mut schema.schema)?;
if type_
.directives
.iter()
.any(|other_directive| other_directive.ptr_eq(&directive))
{
return Err(SingleFederationError::Internal {
message: format!(
"Directive application \"@{}\" already exists on union type \"{}\"",
directive.name, self,
),
}
.into());
}
let name = directive.name.clone();
type_.make_mut().directives.push(directive);
self.insert_directive_name_references(&mut schema.referencers, &name)
}
pub(crate) fn remove_directive_name(&self, schema: &mut FederationSchema, name: &str) {
let Some(type_) = self.try_make_mut(&mut schema.schema) else {
return;
};
self.remove_directive_name_references(&mut schema.referencers, name);
type_
.make_mut()
.directives
.retain(|other_directive| other_directive.name != name);
}
pub(crate) fn insert_member(
&self,
schema: &mut FederationSchema,
name: ComponentName,
) -> Result<(), FederationError> {
let type_ = self.make_mut(&mut schema.schema)?;
type_.make_mut().members.insert(name.clone());
self.insert_member_references(&mut schema.referencers, &name)
}
pub(crate) fn remove_member(&self, schema: &mut FederationSchema, name: &str) {
let Some(type_) = self.try_make_mut(&mut schema.schema) else {
return;
};
self.remove_member_references(&mut schema.referencers, name);
type_
.make_mut()
.members
.retain(|other_type| other_type != name);
}
pub(crate) fn remove_member_recursive(
&self,
schema: &mut FederationSchema,
name: &str,
) -> Result<(), FederationError> {
self.remove_member(schema, name);
let Some(type_) = self.try_get(&schema.schema) else {
return Ok(());
};
if type_.members.is_empty() {
self.remove_recursive(schema)?;
}
Ok(())
}
fn insert_references(
&self,
type_: &Node<UnionType>,
referencers: &mut Referencers,
) -> Result<(), FederationError> {
validate_component_directives(type_.directives.deref())?;
for directive_reference in type_.directives.iter() {
self.insert_directive_name_references(referencers, &directive_reference.name)?;
}
for object_type_reference in type_.members.iter() {
self.insert_member_references(referencers, object_type_reference.deref())?;
}
self.introspection_typename_field()
.insert_references(referencers)
}
fn remove_references(&self, type_: &Node<UnionType>, referencers: &mut Referencers) {
for directive_reference in type_.directives.iter() {
self.remove_directive_name_references(referencers, &directive_reference.name);
}
for object_type_reference in type_.members.iter() {
self.remove_member_references(referencers, object_type_reference.deref());
}
self.introspection_typename_field()
.remove_references(referencers)
}
fn insert_directive_name_references(
&self,
referencers: &mut Referencers,
name: &Name,
) -> Result<(), FederationError> {
let directive_referencers = referencers.directives.get_mut(name).ok_or_else(|| {
SingleFederationError::Internal {
message: format!(
"Union type \"{self}\"'s directive application \"@{name}\" does not refer to an existing directive.",
),
}
})?;
directive_referencers.union_types.insert(self.clone());
Ok(())
}
fn remove_directive_name_references(&self, referencers: &mut Referencers, name: &str) {
let Some(directive_referencers) = referencers.directives.get_mut(name) else {
return;
};
directive_referencers.union_types.shift_remove(self);
}
fn insert_member_references(
&self,
referencers: &mut Referencers,
name: &Name,
) -> Result<(), FederationError> {
let object_type_referencers = referencers.object_types.get_mut(name).ok_or_else(|| {
SingleFederationError::Internal {
message: format!(
"Union type \"{self}\"'s member \"{name}\" does not refer to an existing object.",
),
}
})?;
object_type_referencers.union_types.insert(self.clone());
Ok(())
}
fn remove_member_references(&self, referencers: &mut Referencers, name: &str) {
let Some(object_type_referencers) = referencers.object_types.get_mut(name) else {
return;
};
object_type_referencers.union_types.shift_remove(self);
}
fn rename(&self, schema: &mut FederationSchema, new_name: Name) -> Result<(), FederationError> {
self.make_mut(&mut schema.schema)?.make_mut().name = new_name.clone();
if let Some(union_type_referencers) =
schema.referencers.union_types.swap_remove(&self.type_name)
{
schema
.referencers
.union_types
.insert(new_name, union_type_referencers);
}
Ok(())
}
fn rename_member(
&self,
schema: &mut FederationSchema,
old_name: &Name,
new_name: Name,
) -> Result<(), FederationError> {
let type_ = self.make_mut(&mut schema.schema)?.make_mut();
type_.members.swap_remove(old_name);
type_.members.insert(new_name.into());
Ok(())
}
fn remove_extensions(&self, schema: &mut FederationSchema) -> Result<(), FederationError> {
let type_ = self.make_mut(&mut schema.schema)?.make_mut();
for directive in type_.directives.iter_mut() {
directive.origin = ComponentOrigin::Definition;
}
type_.members = type_
.members
.iter()
.map(|m| {
let mut m = m.clone();
m.origin = ComponentOrigin::Definition;
m
})
.collect();
Ok(())
}
pub(crate) fn has_applied_directive(
&self,
schema: &FederationSchema,
directive_name: &Name,
) -> bool {
if let Some(type_) = self.try_get(schema.schema()) {
return type_
.directives
.iter()
.any(|directive| &directive.name == directive_name);
}
false
}
pub(crate) fn get_all_applied_directives<'schema>(
&self,
schema: &'schema FederationSchema,
) -> Result<impl Iterator<Item = &'schema Component<Directive>>, FederationError> {
Ok(self.get(&schema.schema)?.directives.iter())
}
pub(crate) fn remove_directive(
&self,
schema: &mut FederationSchema,
directive: &Component<Directive>,
) {
let Some(obj) = self.try_make_mut(&mut schema.schema) else {
return;
};
if !obj.directives.iter().any(|other_directive| {
(other_directive.name == directive.name) && !other_directive.ptr_eq(directive)
}) {
self.remove_directive_name_references(&mut schema.referencers, &directive.name);
}
obj.make_mut()
.directives
.retain(|other_directive| !other_directive.ptr_eq(directive));
}
}
impl Display for UnionTypeDefinitionPosition {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.type_name)
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize)]
pub(crate) struct UnionTypenameFieldDefinitionPosition {
pub(crate) type_name: Name,
}
impl UnionTypenameFieldDefinitionPosition {
pub(crate) fn field_name(&self) -> &Name {
&INTROSPECTION_TYPENAME_FIELD_NAME
}
pub(crate) fn parent(&self) -> UnionTypeDefinitionPosition {
UnionTypeDefinitionPosition {
type_name: self.type_name.clone(),
}
}
pub(crate) fn get<'schema>(
&self,
schema: &'schema Schema,
) -> Result<&'schema Component<FieldDefinition>, PositionLookupError> {
let parent = self.parent();
parent.get(schema)?;
schema
.type_field(&self.type_name, self.field_name())
.map_err(|_| {
PositionLookupError::MissingField(
"Union",
self.type_name.clone(),
name!("__typename"),
)
})
}
fn insert_references(&self, referencers: &mut Referencers) -> Result<(), FederationError> {
self.insert_type_references(referencers)?;
Ok(())
}
fn remove_references(&self, referencers: &mut Referencers) {
self.remove_type_references(referencers);
}
fn insert_type_references(&self, referencers: &mut Referencers) -> Result<(), FederationError> {
let output_type_reference = "String";
if let Some(scalar_type_referencers) =
referencers.scalar_types.get_mut(output_type_reference)
{
scalar_type_referencers.union_fields.insert(self.clone());
} else {
return Err(FederationError::internal(format!(
"Schema missing referencers for type \"{output_type_reference}\""
)));
}
Ok(())
}
fn remove_type_references(&self, referencers: &mut Referencers) {
let output_type_reference = "String";
if let Some(scalar_type_referencers) =
referencers.scalar_types.get_mut(output_type_reference)
{
scalar_type_referencers.union_fields.shift_remove(self);
}
}
}
impl Display for UnionTypenameFieldDefinitionPosition {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "{}.{}", self.type_name, self.field_name())
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub(crate) struct EnumTypeDefinitionPosition {
pub(crate) type_name: Name,
}
impl EnumTypeDefinitionPosition {
const EXPECTED: &'static str = "an enum type";
pub(crate) fn value(&self, value_name: Name) -> EnumValueDefinitionPosition {
EnumValueDefinitionPosition {
type_name: self.type_name.clone(),
value_name,
}
}
pub(crate) fn get<'schema>(
&self,
schema: &'schema Schema,
) -> Result<&'schema Node<EnumType>, PositionLookupError> {
schema
.types
.get(&self.type_name)
.ok_or_else(|| PositionLookupError::TypeMissing(self.type_name.clone()))
.and_then(|type_| {
if let ExtendedType::Enum(type_) = type_ {
Ok(type_)
} else {
Err(PositionLookupError::TypeWrongKind(
self.type_name.clone(),
Self::EXPECTED,
))
}
})
}
pub(crate) fn try_get<'schema>(
&self,
schema: &'schema Schema,
) -> Option<&'schema Node<EnumType>> {
self.get(schema).ok()
}
fn make_mut<'schema>(
&self,
schema: &'schema mut Schema,
) -> Result<&'schema mut Node<EnumType>, PositionLookupError> {
schema
.types
.get_mut(&self.type_name)
.ok_or_else(|| PositionLookupError::TypeMissing(self.type_name.clone()))
.and_then(|type_| {
if let ExtendedType::Enum(type_) = type_ {
Ok(type_)
} else {
Err(PositionLookupError::TypeWrongKind(
self.type_name.clone(),
Self::EXPECTED,
))
}
})
}
fn try_make_mut<'schema>(
&self,
schema: &'schema mut Schema,
) -> Option<&'schema mut Node<EnumType>> {
if self.try_get(schema).is_some() {
self.make_mut(schema).ok()
} else {
None
}
}
pub(crate) fn pre_insert(&self, schema: &mut FederationSchema) -> Result<(), FederationError> {
if schema.referencers.contains_type_name(&self.type_name) {
bail!(r#"Type "{self}" has already been pre-inserted"#);
}
schema
.referencers
.enum_types
.insert(self.type_name.clone(), Default::default());
Ok(())
}
pub(crate) fn insert(
&self,
schema: &mut FederationSchema,
type_: Node<EnumType>,
) -> Result<(), FederationError> {
if self.type_name != type_.name {
return Err(SingleFederationError::Internal {
message: format!("Enum type \"{}\" given type named \"{}\"", self, type_.name,),
}
.into());
}
if !schema.referencers.enum_types.contains_key(&self.type_name) {
bail!(r#"Type "{self}" has not been pre-inserted"#);
}
if schema.schema.types.contains_key(&self.type_name) {
bail!(r#"Type "{self}" already exists in schema"#);
}
schema
.schema
.types
.insert(self.type_name.clone(), ExtendedType::Enum(type_));
self.insert_references(self.get(&schema.schema)?, &mut schema.referencers)
}
pub(crate) fn remove(
&self,
schema: &mut FederationSchema,
) -> Result<Option<EnumTypeReferencers>, FederationError> {
let Some(referencers) = self.remove_internal(schema)? else {
return Ok(None);
};
for field in &referencers.object_fields {
field.remove(schema)?;
}
for argument in &referencers.object_field_arguments {
argument.remove(schema)?;
}
for field in &referencers.interface_fields {
field.remove(schema)?;
}
for argument in &referencers.interface_field_arguments {
argument.remove(schema)?;
}
for field in &referencers.input_object_fields {
field.remove(schema)?;
}
for argument in &referencers.directive_arguments {
argument.remove(schema)?;
}
Ok(Some(referencers))
}
fn remove_internal(
&self,
schema: &mut FederationSchema,
) -> Result<Option<EnumTypeReferencers>, FederationError> {
let Some(type_) = self.try_get(&schema.schema) else {
return Ok(None);
};
self.remove_references(type_, &mut schema.referencers)?;
schema.schema.types.shift_remove(&self.type_name);
Ok(Some(
schema
.referencers
.enum_types
.shift_remove(&self.type_name)
.ok_or_else(|| SingleFederationError::Internal {
message: format!("Schema missing referencers for type \"{self}\""),
})?,
))
}
pub(crate) fn insert_directive(
&self,
schema: &mut FederationSchema,
directive: Component<Directive>,
) -> Result<(), FederationError> {
let type_ = self.make_mut(&mut schema.schema)?;
if type_
.directives
.iter()
.any(|other_directive| other_directive.ptr_eq(&directive))
{
return Err(SingleFederationError::Internal {
message: format!(
"Directive application \"@{}\" already exists on enum type \"{}\"",
directive.name, self,
),
}
.into());
}
let name = directive.name.clone();
type_.make_mut().directives.push(directive);
self.insert_directive_name_references(&mut schema.referencers, &name)
}
pub(crate) fn remove_directive_name(&self, schema: &mut FederationSchema, name: &str) {
let Some(type_) = self.try_make_mut(&mut schema.schema) else {
return;
};
self.remove_directive_name_references(&mut schema.referencers, name);
type_
.make_mut()
.directives
.retain(|other_directive| other_directive.name != name);
}
fn insert_references(
&self,
type_: &Node<EnumType>,
referencers: &mut Referencers,
) -> Result<(), FederationError> {
validate_component_directives(type_.directives.deref())?;
for directive_reference in type_.directives.iter() {
self.insert_directive_name_references(referencers, &directive_reference.name)?;
}
for (value_name, value) in type_.values.iter() {
self.value(value_name.clone())
.insert_references(value, referencers)?;
}
Ok(())
}
fn remove_references(
&self,
type_: &Node<EnumType>,
referencers: &mut Referencers,
) -> Result<(), FederationError> {
for directive_reference in type_.directives.iter() {
self.remove_directive_name_references(referencers, &directive_reference.name);
}
for (value_name, value) in type_.values.iter() {
self.value(value_name.clone())
.remove_references(value, referencers)?;
}
Ok(())
}
fn insert_directive_name_references(
&self,
referencers: &mut Referencers,
name: &Name,
) -> Result<(), FederationError> {
let directive_referencers = referencers.directives.get_mut(name).ok_or_else(|| {
SingleFederationError::Internal {
message: format!(
"Enum type \"{self}\"'s directive application \"@{name}\" does not refer to an existing directive.",
),
}
})?;
directive_referencers.enum_types.insert(self.clone());
Ok(())
}
fn remove_directive_name_references(&self, referencers: &mut Referencers, name: &str) {
let Some(directive_referencers) = referencers.directives.get_mut(name) else {
return;
};
directive_referencers.enum_types.shift_remove(self);
}
fn rename(&self, schema: &mut FederationSchema, new_name: Name) -> Result<(), FederationError> {
self.make_mut(&mut schema.schema)?.make_mut().name = new_name.clone();
if let Some(enum_type_referencers) =
schema.referencers.enum_types.swap_remove(&self.type_name)
{
for pos in enum_type_referencers.object_fields.iter() {
pos.rename_type(schema, new_name.clone())?;
}
for pos in enum_type_referencers.object_field_arguments.iter() {
pos.rename_type(schema, new_name.clone())?;
}
for pos in enum_type_referencers.interface_fields.iter() {
pos.rename_type(schema, new_name.clone())?;
}
for pos in enum_type_referencers.interface_field_arguments.iter() {
pos.rename_type(schema, new_name.clone())?;
}
for pos in enum_type_referencers.input_object_fields.iter() {
pos.rename_type(schema, new_name.clone())?;
}
schema
.referencers
.enum_types
.insert(new_name, enum_type_referencers);
}
Ok(())
}
fn remove_extensions(&self, schema: &mut FederationSchema) -> Result<(), FederationError> {
let type_ = self.make_mut(&mut schema.schema)?.make_mut();
for directive in type_.directives.iter_mut() {
directive.origin = ComponentOrigin::Definition;
}
for (_, v) in type_.values.iter_mut() {
v.origin = ComponentOrigin::Definition;
}
Ok(())
}
pub(crate) fn has_applied_directive(
&self,
schema: &FederationSchema,
directive_name: &Name,
) -> bool {
if let Some(type_) = self.try_get(schema.schema()) {
return type_
.directives
.iter()
.any(|directive| &directive.name == directive_name);
}
false
}
pub(crate) fn get_all_applied_directives<'schema>(
&self,
schema: &'schema FederationSchema,
) -> Result<impl Iterator<Item = &'schema Component<Directive>>, FederationError> {
Ok(self.get(&schema.schema)?.directives.iter())
}
pub(crate) fn remove_directive(
&self,
schema: &mut FederationSchema,
directive: &Component<Directive>,
) {
let Some(obj) = self.try_make_mut(&mut schema.schema) else {
return;
};
if !obj.directives.iter().any(|other_directive| {
(other_directive.name == directive.name) && !other_directive.ptr_eq(directive)
}) {
self.remove_directive_name_references(&mut schema.referencers, &directive.name);
}
obj.make_mut()
.directives
.retain(|other_directive| !other_directive.ptr_eq(directive));
}
}
impl Display for EnumTypeDefinitionPosition {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.type_name)
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub(crate) struct EnumValueDefinitionPosition {
pub(crate) type_name: Name,
pub(crate) value_name: Name,
}
impl EnumValueDefinitionPosition {
pub(crate) fn get_all_applied_directives<'schema>(
&self,
schema: &'schema FederationSchema,
) -> Result<impl Iterator<Item = &'schema Node<Directive>>, FederationError> {
Ok(self.get(&schema.schema)?.directives.iter())
}
pub(crate) fn parent(&self) -> EnumTypeDefinitionPosition {
EnumTypeDefinitionPosition {
type_name: self.type_name.clone(),
}
}
pub(crate) fn get<'schema>(
&self,
schema: &'schema Schema,
) -> Result<&'schema Component<EnumValueDefinition>, PositionLookupError> {
let parent = self.parent();
let type_ = parent.get(schema)?;
type_
.values
.get(&self.value_name)
.ok_or_else(|| PositionLookupError::MissingValue(self.clone()))
}
pub(crate) fn try_get<'schema>(
&self,
schema: &'schema Schema,
) -> Option<&'schema Component<EnumValueDefinition>> {
self.get(schema).ok()
}
fn make_mut<'schema>(
&self,
schema: &'schema mut Schema,
) -> Result<&'schema mut Component<EnumValueDefinition>, PositionLookupError> {
let parent = self.parent();
let type_ = parent.make_mut(schema)?.make_mut();
type_
.values
.get_mut(&self.value_name)
.ok_or_else(|| PositionLookupError::MissingValue(self.clone()))
}
fn try_make_mut<'schema>(
&self,
schema: &'schema mut Schema,
) -> Option<&'schema mut Component<EnumValueDefinition>> {
if self.try_get(schema).is_some() {
self.make_mut(schema).ok()
} else {
None
}
}
pub(crate) fn insert(
&self,
schema: &mut FederationSchema,
value: Component<EnumValueDefinition>,
) -> Result<(), FederationError> {
if self.value_name != value.value {
return Err(SingleFederationError::Internal {
message: format!(
"Enum value \"{}\" given argument named \"{}\"",
self, value.value,
),
}
.into());
}
if self.try_get(&schema.schema).is_some() {
bail!(r#"Enum value "{self}" already exists in schema"#);
}
self.parent()
.make_mut(&mut schema.schema)?
.make_mut()
.values
.insert(self.value_name.clone(), value);
self.insert_references(self.get(&schema.schema)?, &mut schema.referencers)
}
pub(crate) fn remove(&self, schema: &mut FederationSchema) -> Result<(), FederationError> {
let Some(value) = self.try_get(&schema.schema) else {
return Ok(());
};
self.remove_references(value, &mut schema.referencers)?;
self.parent()
.make_mut(&mut schema.schema)?
.make_mut()
.values
.shift_remove(&self.value_name);
Ok(())
}
pub(crate) fn insert_directive(
&self,
schema: &mut FederationSchema,
directive: Node<Directive>,
) -> Result<(), FederationError> {
let value = self.make_mut(&mut schema.schema)?;
if value
.directives
.iter()
.any(|other_directive| other_directive.ptr_eq(&directive))
{
return Err(SingleFederationError::Internal {
message: format!(
"Directive application \"@{}\" already exists on enum value \"{}\"",
directive.name, self,
),
}
.into());
}
let name = directive.name.clone();
value.make_mut().directives.push(directive);
self.insert_directive_name_references(&mut schema.referencers, &name)
}
pub(crate) fn remove_directive_name(&self, schema: &mut FederationSchema, name: &str) {
let Some(value) = self.try_make_mut(&mut schema.schema) else {
return;
};
self.remove_directive_name_references(&mut schema.referencers, name);
value
.make_mut()
.directives
.retain(|other_directive| other_directive.name != name);
}
fn insert_references(
&self,
value: &Component<EnumValueDefinition>,
referencers: &mut Referencers,
) -> Result<(), FederationError> {
if is_graphql_reserved_name(&self.value_name) {
bail!(r#"Cannot insert reserved enum value "{self}""#);
}
validate_node_directives(value.directives.deref())?;
for directive_reference in value.directives.iter() {
self.insert_directive_name_references(referencers, &directive_reference.name)?;
}
Ok(())
}
fn remove_references(
&self,
value: &Component<EnumValueDefinition>,
referencers: &mut Referencers,
) -> Result<(), FederationError> {
if is_graphql_reserved_name(&self.value_name) {
bail!(r#"Cannot remove reserved enum value "{self}""#);
}
for directive_reference in value.directives.iter() {
self.remove_directive_name_references(referencers, &directive_reference.name);
}
Ok(())
}
fn insert_directive_name_references(
&self,
referencers: &mut Referencers,
name: &Name,
) -> Result<(), FederationError> {
let directive_referencers = referencers.directives.get_mut(name).ok_or_else(|| {
SingleFederationError::Internal {
message: format!(
"Enum value \"{self}\"'s directive application \"@{name}\" does not refer to an existing directive.",
),
}
})?;
directive_referencers.enum_values.insert(self.clone());
Ok(())
}
fn remove_directive_name_references(&self, referencers: &mut Referencers, name: &str) {
let Some(directive_referencers) = referencers.directives.get_mut(name) else {
return;
};
directive_referencers.enum_values.shift_remove(self);
}
}
impl Display for EnumValueDefinitionPosition {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "{}.{}", self.type_name, self.value_name)
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub(crate) struct InputObjectTypeDefinitionPosition {
pub(crate) type_name: Name,
}
impl InputObjectTypeDefinitionPosition {
const EXPECTED: &'static str = "an input object type";
pub(crate) fn field(&self, field_name: Name) -> InputObjectFieldDefinitionPosition {
InputObjectFieldDefinitionPosition {
type_name: self.type_name.clone(),
field_name,
}
}
pub(crate) fn get<'schema>(
&self,
schema: &'schema Schema,
) -> Result<&'schema Node<InputObjectType>, PositionLookupError> {
schema
.types
.get(&self.type_name)
.ok_or_else(|| PositionLookupError::TypeMissing(self.type_name.clone()))
.and_then(|type_| {
if let ExtendedType::InputObject(type_) = type_ {
Ok(type_)
} else {
Err(PositionLookupError::TypeWrongKind(
self.type_name.clone(),
Self::EXPECTED,
))
}
})
}
pub(crate) fn try_get<'schema>(
&self,
schema: &'schema Schema,
) -> Option<&'schema Node<InputObjectType>> {
self.get(schema).ok()
}
fn make_mut<'schema>(
&self,
schema: &'schema mut Schema,
) -> Result<&'schema mut Node<InputObjectType>, PositionLookupError> {
schema
.types
.get_mut(&self.type_name)
.ok_or_else(|| PositionLookupError::TypeMissing(self.type_name.clone()))
.and_then(|type_| {
if let ExtendedType::InputObject(type_) = type_ {
Ok(type_)
} else {
Err(PositionLookupError::TypeWrongKind(
self.type_name.clone(),
Self::EXPECTED,
))
}
})
}
fn try_make_mut<'schema>(
&self,
schema: &'schema mut Schema,
) -> Option<&'schema mut Node<InputObjectType>> {
if self.try_get(schema).is_some() {
self.make_mut(schema).ok()
} else {
None
}
}
pub(crate) fn pre_insert(&self, schema: &mut FederationSchema) -> Result<(), FederationError> {
if schema.referencers.contains_type_name(&self.type_name) {
bail!(r#"Type "{self}" has already been pre-inserted"#);
}
schema
.referencers
.input_object_types
.insert(self.type_name.clone(), Default::default());
Ok(())
}
pub(crate) fn insert(
&self,
schema: &mut FederationSchema,
type_: Node<InputObjectType>,
) -> Result<(), FederationError> {
if self.type_name != type_.name {
return Err(SingleFederationError::Internal {
message: format!(
"Input object type \"{}\" given type named \"{}\"",
self, type_.name,
),
}
.into());
}
if !schema
.referencers
.input_object_types
.contains_key(&self.type_name)
{
bail!(r#"Type "{self}" has not been pre-inserted"#);
}
if schema.schema.types.contains_key(&self.type_name) {
bail!(r#"Type "{self}" already exists in schema"#);
}
schema
.schema
.types
.insert(self.type_name.clone(), ExtendedType::InputObject(type_));
self.insert_references(self.get(&schema.schema)?, &mut schema.referencers)
}
pub(crate) fn remove(
&self,
schema: &mut FederationSchema,
) -> Result<Option<InputObjectTypeReferencers>, FederationError> {
let Some(referencers) = self.remove_internal(schema)? else {
return Ok(None);
};
for argument in &referencers.object_field_arguments {
argument.remove(schema)?;
}
for argument in &referencers.interface_field_arguments {
argument.remove(schema)?;
}
for field in &referencers.input_object_fields {
field.remove(schema)?;
}
for argument in &referencers.directive_arguments {
argument.remove(schema)?;
}
Ok(Some(referencers))
}
pub(crate) fn remove_recursive(
&self,
schema: &mut FederationSchema,
) -> Result<(), FederationError> {
let Some(referencers) = self.remove_internal(schema)? else {
return Ok(());
};
for argument in referencers.object_field_arguments {
argument.remove(schema)?;
}
for argument in referencers.interface_field_arguments {
argument.remove(schema)?;
}
for field in referencers.input_object_fields {
field.remove_recursive(schema)?;
}
for argument in referencers.directive_arguments {
argument.remove(schema)?;
}
Ok(())
}
fn remove_internal(
&self,
schema: &mut FederationSchema,
) -> Result<Option<InputObjectTypeReferencers>, FederationError> {
let Some(type_) = self.try_get(&schema.schema) else {
return Ok(None);
};
self.remove_references(type_, &mut schema.referencers)?;
schema.schema.types.shift_remove(&self.type_name);
Ok(Some(
schema
.referencers
.input_object_types
.shift_remove(&self.type_name)
.ok_or_else(|| SingleFederationError::Internal {
message: format!("Schema missing referencers for type \"{self}\""),
})?,
))
}
pub(crate) fn insert_directive(
&self,
schema: &mut FederationSchema,
directive: Component<Directive>,
) -> Result<(), FederationError> {
let type_ = self.make_mut(&mut schema.schema)?;
if type_
.directives
.iter()
.any(|other_directive| other_directive.ptr_eq(&directive))
{
return Err(SingleFederationError::Internal {
message: format!(
"Directive application \"@{}\" already exists on input object type \"{}\"",
directive.name, self,
),
}
.into());
}
let name = directive.name.clone();
type_.make_mut().directives.push(directive);
self.insert_directive_name_references(&mut schema.referencers, &name)
}
pub(crate) fn remove_directive_name(&self, schema: &mut FederationSchema, name: &str) {
let Some(type_) = self.try_make_mut(&mut schema.schema) else {
return;
};
self.remove_directive_name_references(&mut schema.referencers, name);
type_
.make_mut()
.directives
.retain(|other_directive| other_directive.name != name);
}
fn insert_references(
&self,
type_: &Node<InputObjectType>,
referencers: &mut Referencers,
) -> Result<(), FederationError> {
validate_component_directives(type_.directives.deref())?;
for directive_reference in type_.directives.iter() {
self.insert_directive_name_references(referencers, &directive_reference.name)?;
}
for (field_name, field) in type_.fields.iter() {
self.field(field_name.clone())
.insert_references(field, referencers)?;
}
Ok(())
}
fn remove_references(
&self,
type_: &Node<InputObjectType>,
referencers: &mut Referencers,
) -> Result<(), FederationError> {
for directive_reference in type_.directives.iter() {
self.remove_directive_name_references(referencers, &directive_reference.name);
}
for (field_name, field) in type_.fields.iter() {
self.field(field_name.clone())
.remove_references(field, referencers)?;
}
Ok(())
}
fn insert_directive_name_references(
&self,
referencers: &mut Referencers,
name: &Name,
) -> Result<(), FederationError> {
let directive_referencers = referencers.directives.get_mut(name).ok_or_else(|| {
SingleFederationError::Internal {
message: format!(
"Input object type \"{self}\"'s directive application \"@{name}\" does not refer to an existing directive.",
),
}
})?;
directive_referencers
.input_object_types
.insert(self.clone());
Ok(())
}
fn remove_directive_name_references(&self, referencers: &mut Referencers, name: &str) {
let Some(directive_referencers) = referencers.directives.get_mut(name) else {
return;
};
directive_referencers.input_object_types.shift_remove(self);
}
fn rename(&self, schema: &mut FederationSchema, new_name: Name) -> Result<(), FederationError> {
self.make_mut(&mut schema.schema)?.make_mut().name = new_name.clone();
if let Some(input_object_type_referencers) = schema
.referencers
.input_object_types
.swap_remove(&self.type_name)
{
schema
.referencers
.input_object_types
.insert(new_name, input_object_type_referencers);
}
Ok(())
}
fn remove_extensions(&self, schema: &mut FederationSchema) -> Result<(), FederationError> {
let type_ = self.make_mut(&mut schema.schema)?.make_mut();
for directive in type_.directives.iter_mut() {
directive.origin = ComponentOrigin::Definition;
}
for (_, field) in type_.fields.iter_mut() {
field.origin = ComponentOrigin::Definition;
}
Ok(())
}
pub(crate) fn has_applied_directive(
&self,
schema: &FederationSchema,
directive_name: &Name,
) -> bool {
if let Some(type_) = self.try_get(schema.schema()) {
return type_
.directives
.iter()
.any(|directive| &directive.name == directive_name);
}
false
}
pub(crate) fn get_all_applied_directives<'schema>(
&self,
schema: &'schema FederationSchema,
) -> Result<impl Iterator<Item = &'schema Component<Directive>>, FederationError> {
Ok(self.get(&schema.schema)?.directives.iter())
}
pub(crate) fn remove_directive(
&self,
schema: &mut FederationSchema,
directive: &Component<Directive>,
) {
let Some(obj) = self.try_make_mut(&mut schema.schema) else {
return;
};
if !obj.directives.iter().any(|other_directive| {
(other_directive.name == directive.name) && !other_directive.ptr_eq(directive)
}) {
self.remove_directive_name_references(&mut schema.referencers, &directive.name);
}
obj.make_mut()
.directives
.retain(|other_directive| !other_directive.ptr_eq(directive));
}
}
impl Display for InputObjectTypeDefinitionPosition {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.type_name)
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub(crate) struct InputObjectFieldDefinitionPosition {
pub(crate) type_name: Name,
pub(crate) field_name: Name,
}
impl InputObjectFieldDefinitionPosition {
pub(crate) fn get_all_applied_directives<'schema>(
&self,
schema: &'schema FederationSchema,
) -> Result<impl Iterator<Item = &'schema Node<Directive>>, FederationError> {
Ok(self.get(&schema.schema)?.directives.iter())
}
pub(crate) fn has_applied_directive(
&self,
schema: &FederationSchema,
directive_name: &Name,
) -> bool {
!self
.get_applied_directives(schema, directive_name)
.is_empty()
}
pub(crate) fn parent(&self) -> InputObjectTypeDefinitionPosition {
InputObjectTypeDefinitionPosition {
type_name: self.type_name.clone(),
}
}
pub(crate) fn get<'schema>(
&self,
schema: &'schema Schema,
) -> Result<&'schema Component<InputValueDefinition>, PositionLookupError> {
let parent = self.parent();
let type_ = parent.get(schema)?;
type_.fields.get(&self.field_name).ok_or_else(|| {
PositionLookupError::MissingField(
"Input object",
self.type_name.clone(),
self.field_name.clone(),
)
})
}
pub(crate) fn try_get<'schema>(
&self,
schema: &'schema Schema,
) -> Option<&'schema Component<InputValueDefinition>> {
self.get(schema).ok()
}
pub(crate) fn make_mut<'schema>(
&self,
schema: &'schema mut Schema,
) -> Result<&'schema mut Component<InputValueDefinition>, PositionLookupError> {
let parent = self.parent();
let type_ = parent.make_mut(schema)?.make_mut();
type_.fields.get_mut(&self.field_name).ok_or_else(|| {
PositionLookupError::MissingField(
"Input object",
self.type_name.clone(),
self.field_name.clone(),
)
})
}
fn try_make_mut<'schema>(
&self,
schema: &'schema mut Schema,
) -> Option<&'schema mut Component<InputValueDefinition>> {
if self.try_get(schema).is_some() {
self.make_mut(schema).ok()
} else {
None
}
}
pub(crate) fn insert(
&self,
schema: &mut FederationSchema,
field: Component<InputValueDefinition>,
) -> Result<(), FederationError> {
if self.field_name != field.name {
return Err(SingleFederationError::Internal {
message: format!(
"Input object field \"{}\" given field named \"{}\"",
self, field.name,
),
}
.into());
}
if self.try_get(&schema.schema).is_some() {
bail!(r#"Input object field "{self}" already exists in schema"#);
}
self.parent()
.make_mut(&mut schema.schema)?
.make_mut()
.fields
.insert(self.field_name.clone(), field);
self.insert_references(self.get(&schema.schema)?, &mut schema.referencers)
}
pub(crate) fn remove(&self, schema: &mut FederationSchema) -> Result<(), FederationError> {
let Some(field) = self.try_get(&schema.schema) else {
return Ok(());
};
self.remove_references(field, &mut schema.referencers)?;
self.parent()
.make_mut(&mut schema.schema)?
.make_mut()
.fields
.shift_remove(&self.field_name);
Ok(())
}
pub(crate) fn remove_recursive(
&self,
schema: &mut FederationSchema,
) -> Result<(), FederationError> {
self.remove(schema)?;
let parent = self.parent();
let Some(type_) = parent.try_get(&schema.schema) else {
return Ok(());
};
if type_.fields.is_empty() {
parent.remove_recursive(schema)?;
}
Ok(())
}
pub(crate) fn insert_directive(
&self,
schema: &mut FederationSchema,
directive: Node<Directive>,
) -> Result<(), FederationError> {
let field = self.make_mut(&mut schema.schema)?;
if field
.directives
.iter()
.any(|other_directive| other_directive.ptr_eq(&directive))
{
return Err(SingleFederationError::Internal {
message: format!(
"Directive application \"@{}\" already exists on input object field \"{}\"",
directive.name, self,
),
}
.into());
}
let name = directive.name.clone();
field.make_mut().directives.push(directive);
self.insert_directive_name_references(&mut schema.referencers, &name)
}
pub(crate) fn remove_directive_name(&self, schema: &mut FederationSchema, name: &str) {
let Some(field) = self.try_make_mut(&mut schema.schema) else {
return;
};
self.remove_directive_name_references(&mut schema.referencers, name);
field
.make_mut()
.directives
.retain(|other_directive| other_directive.name != name);
}
fn insert_references(
&self,
field: &Component<InputValueDefinition>,
referencers: &mut Referencers,
) -> Result<(), FederationError> {
if is_graphql_reserved_name(&self.field_name) {
return Ok(());
}
validate_node_directives(field.directives.deref())?;
for directive_reference in field.directives.iter() {
self.insert_directive_name_references(referencers, &directive_reference.name)?;
}
self.insert_type_references(field, referencers)
}
fn remove_references(
&self,
field: &Component<InputValueDefinition>,
referencers: &mut Referencers,
) -> Result<(), FederationError> {
if is_graphql_reserved_name(&self.field_name) {
bail!(r#"Cannot remove reserved input object field "{self}""#);
}
for directive_reference in field.directives.iter() {
self.remove_directive_name_references(referencers, &directive_reference.name);
}
self.remove_type_references(field, referencers);
Ok(())
}
fn insert_directive_name_references(
&self,
referencers: &mut Referencers,
name: &Name,
) -> Result<(), FederationError> {
let directive_referencers = referencers.directives.get_mut(name).ok_or_else(|| {
SingleFederationError::Internal {
message: format!(
"Input object field \"{self}\"'s directive application \"@{name}\" does not refer to an existing directive.",
),
}
})?;
directive_referencers
.input_object_fields
.insert(self.clone());
Ok(())
}
fn remove_directive_name_references(&self, referencers: &mut Referencers, name: &str) {
let Some(directive_referencers) = referencers.directives.get_mut(name) else {
return;
};
directive_referencers.input_object_fields.shift_remove(self);
}
fn insert_type_references(
&self,
argument: &Node<InputValueDefinition>,
referencers: &mut Referencers,
) -> Result<(), FederationError> {
let input_type_reference = argument.ty.inner_named_type();
if let Some(scalar_type_referencers) =
referencers.scalar_types.get_mut(input_type_reference)
{
scalar_type_referencers
.input_object_fields
.insert(self.clone());
} else if let Some(enum_type_referencers) =
referencers.enum_types.get_mut(input_type_reference)
{
enum_type_referencers
.input_object_fields
.insert(self.clone());
} else if let Some(input_object_type_referencers) =
referencers.input_object_types.get_mut(input_type_reference)
{
input_object_type_referencers
.input_object_fields
.insert(self.clone());
} else {
return Err(FederationError::internal(format!(
"Input object field \"{}\"'s inner type \"{}\" does not refer to an existing input type.",
self,
input_type_reference.deref(),
)));
}
Ok(())
}
fn remove_type_references(
&self,
field: &Component<InputValueDefinition>,
referencers: &mut Referencers,
) {
let input_type_reference = field.ty.inner_named_type();
if let Some(scalar_type_referencers) =
referencers.scalar_types.get_mut(input_type_reference)
{
scalar_type_referencers
.input_object_fields
.shift_remove(self);
} else if let Some(enum_type_referencers) =
referencers.enum_types.get_mut(input_type_reference)
{
enum_type_referencers.input_object_fields.shift_remove(self);
} else if let Some(input_object_type_referencers) =
referencers.input_object_types.get_mut(input_type_reference)
{
input_object_type_referencers
.input_object_fields
.shift_remove(self);
}
}
fn rename_type(
&self,
schema: &mut FederationSchema,
new_name: Name,
) -> Result<(), FederationError> {
self.make_mut(&mut schema.schema)?.make_mut().name = new_name;
Ok(())
}
}
impl Display for InputObjectFieldDefinitionPosition {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "{}.{}", self.type_name, self.field_name)
}
}
impl HasDefaultValue for InputObjectFieldDefinitionPosition {
fn is_input_field() -> bool {
true
}
fn get_default_value<'schema>(
&self,
schema: &'schema FederationSchema,
) -> Option<&'schema Node<ast::Value>> {
self.try_get(&schema.schema)
.and_then(|field| field.default_value.as_ref())
}
fn set_default_value(
&self,
schema: &mut FederationSchema,
default: Option<Node<ast::Value>>,
) -> Result<(), FederationError> {
let field = self.make_mut(&mut schema.schema)?;
field.make_mut().default_value = default;
Ok(())
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub(crate) struct DirectiveDefinitionPosition {
pub(crate) directive_name: Name,
}
impl DirectiveDefinitionPosition {
pub(crate) fn argument(&self, argument_name: Name) -> DirectiveArgumentDefinitionPosition {
DirectiveArgumentDefinitionPosition {
directive_name: self.directive_name.clone(),
argument_name,
}
}
pub(crate) fn get<'schema>(
&self,
schema: &'schema Schema,
) -> Result<&'schema Node<DirectiveDefinition>, PositionLookupError> {
schema
.directive_definitions
.get(&self.directive_name)
.ok_or_else(|| PositionLookupError::DirectiveMissing(self.clone()))
}
pub(crate) fn try_get<'schema>(
&self,
schema: &'schema Schema,
) -> Option<&'schema Node<DirectiveDefinition>> {
self.get(schema).ok()
}
fn make_mut<'schema>(
&self,
schema: &'schema mut Schema,
) -> Result<&'schema mut Node<DirectiveDefinition>, PositionLookupError> {
schema
.directive_definitions
.get_mut(&self.directive_name)
.ok_or_else(|| PositionLookupError::DirectiveMissing(self.clone()))
}
pub(crate) fn pre_insert(&self, schema: &mut FederationSchema) -> Result<(), FederationError> {
if schema
.referencers
.directives
.contains_key(&self.directive_name)
{
bail!(r#"Directive "{self}" has already been pre-inserted"#);
}
schema
.referencers
.directives
.insert(self.directive_name.clone(), Default::default());
Ok(())
}
pub(crate) fn insert(
&self,
schema: &mut FederationSchema,
directive: Node<DirectiveDefinition>,
) -> Result<(), FederationError> {
if !schema
.referencers
.directives
.contains_key(&self.directive_name)
{
bail!(r#"Directive "{self}" has not been pre-inserted"#);
}
if schema
.schema
.directive_definitions
.contains_key(&self.directive_name)
{
bail!(r#"Directive "{self}" already exists in schema"#);
}
schema
.schema
.directive_definitions
.insert(self.directive_name.clone(), directive);
self.insert_references(self.get(&schema.schema)?, &mut schema.referencers)
}
pub(crate) fn remove(
&self,
schema: &mut FederationSchema,
) -> Result<Option<DirectiveReferencers>, FederationError> {
let Some(referencers) = self.remove_internal(schema)? else {
return Ok(None);
};
if let Some(schema_definition) = &referencers.schema {
schema_definition.remove_directive_name(schema, &self.directive_name)?;
}
for type_ in &referencers.scalar_types {
type_.remove_directive_name(schema, &self.directive_name);
}
for type_ in &referencers.object_types {
type_.remove_directive_name(schema, &self.directive_name);
}
for field in &referencers.object_fields {
field.remove_directive_name(schema, &self.directive_name);
}
for argument in &referencers.object_field_arguments {
argument.remove_directive_name(schema, &self.directive_name);
}
for type_ in &referencers.interface_types {
type_.remove_directive_name(schema, &self.directive_name);
}
for field in &referencers.interface_fields {
field.remove_directive_name(schema, &self.directive_name);
}
for argument in &referencers.interface_field_arguments {
argument.remove_directive_name(schema, &self.directive_name);
}
for type_ in &referencers.union_types {
type_.remove_directive_name(schema, &self.directive_name);
}
for type_ in &referencers.enum_types {
type_.remove_directive_name(schema, &self.directive_name);
}
for value in &referencers.enum_values {
value.remove_directive_name(schema, &self.directive_name);
}
for type_ in &referencers.input_object_types {
type_.remove_directive_name(schema, &self.directive_name);
}
for field in &referencers.input_object_fields {
field.remove_directive_name(schema, &self.directive_name);
}
for argument in &referencers.directive_arguments {
argument.remove_directive_name(schema, &self.directive_name);
}
Ok(Some(referencers))
}
fn remove_internal(
&self,
schema: &mut FederationSchema,
) -> Result<Option<DirectiveReferencers>, FederationError> {
let Some(directive) = self.try_get(&schema.schema) else {
return Ok(None);
};
self.remove_references(directive, &mut schema.referencers)?;
schema
.schema
.directive_definitions
.shift_remove(&self.directive_name);
Ok(Some(
schema
.referencers
.directives
.shift_remove(&self.directive_name)
.ok_or_else(|| SingleFederationError::Internal {
message: format!("Schema missing referencers for directive \"{self}\""),
})?,
))
}
fn insert_references(
&self,
directive: &Node<DirectiveDefinition>,
referencers: &mut Referencers,
) -> Result<(), FederationError> {
for argument in directive.arguments.iter() {
self.argument(argument.name.clone())
.insert_references(argument, referencers)?;
}
Ok(())
}
fn remove_references(
&self,
directive: &Node<DirectiveDefinition>,
referencers: &mut Referencers,
) -> Result<(), FederationError> {
for argument in directive.arguments.iter() {
self.argument(argument.name.clone())
.remove_references(argument, referencers)?;
}
Ok(())
}
pub(crate) fn set_repeatable(
&self,
schema: &mut FederationSchema,
repeatable: bool,
) -> Result<(), FederationError> {
self.make_mut(&mut schema.schema)?.make_mut().repeatable = repeatable;
Ok(())
}
pub(crate) fn set_locations(
&self,
schema: &mut FederationSchema,
locations: Vec<DirectiveLocation>,
) -> Result<(), FederationError> {
self.make_mut(&mut schema.schema)?.make_mut().locations = locations;
Ok(())
}
}
impl Display for DirectiveDefinitionPosition {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "@{}", self.directive_name)
}
}
impl HasArguments for DirectiveDefinitionPosition {
type ArgumentPosition = DirectiveArgumentDefinitionPosition;
fn argument_position(&self, name: Name) -> Self::ArgumentPosition {
self.argument(name)
}
fn get_argument<'schema>(
&self,
schema: &'schema FederationSchema,
name: &Name,
) -> Option<&'schema Node<InputValueDefinition>> {
self.argument_position(name.clone()).try_get(&schema.schema)
}
fn get_arguments<'schema>(
&self,
schema: &'schema FederationSchema,
) -> Result<&'schema Vec<Node<InputValueDefinition>>, FederationError> {
Ok(&self.get(&schema.schema)?.arguments)
}
fn insert_argument(
&self,
schema: &mut FederationSchema,
arg: Node<InputValueDefinition>,
) -> Result<(), FederationError> {
self.argument_position(arg.name.clone()).insert(schema, arg)
}
fn remove_argument(
&self,
schema: &mut FederationSchema,
name: &Name,
) -> Result<(), FederationError> {
self.argument_position(name.clone()).remove(schema)
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub(crate) struct DirectiveArgumentDefinitionPosition {
pub(crate) directive_name: Name,
pub(crate) argument_name: Name,
}
impl DirectiveArgumentDefinitionPosition {
pub(crate) fn get_all_applied_directives<'schema>(
&self,
schema: &'schema FederationSchema,
) -> Result<impl Iterator<Item = &'schema Node<Directive>>, FederationError> {
Ok(self.get(&schema.schema)?.directives.iter())
}
pub(crate) fn parent(&self) -> DirectiveDefinitionPosition {
DirectiveDefinitionPosition {
directive_name: self.directive_name.clone(),
}
}
pub(crate) fn get<'schema>(
&self,
schema: &'schema Schema,
) -> Result<&'schema Node<InputValueDefinition>, PositionLookupError> {
let parent = self.parent();
let type_ = parent.get(schema)?;
type_
.arguments
.iter()
.find(|a| a.name == self.argument_name)
.ok_or_else(|| PositionLookupError::MissingDirectiveArgument(self.clone()))
}
pub(crate) fn try_get<'schema>(
&self,
schema: &'schema Schema,
) -> Option<&'schema Node<InputValueDefinition>> {
self.get(schema).ok()
}
fn make_mut<'schema>(
&self,
schema: &'schema mut Schema,
) -> Result<&'schema mut Node<InputValueDefinition>, PositionLookupError> {
let parent = self.parent();
let type_ = parent.make_mut(schema)?.make_mut();
type_
.arguments
.iter_mut()
.find(|a| a.name == self.argument_name)
.ok_or_else(|| PositionLookupError::MissingDirectiveArgument(self.clone()))
}
fn try_make_mut<'schema>(
&self,
schema: &'schema mut Schema,
) -> Option<&'schema mut Node<InputValueDefinition>> {
if self.try_get(schema).is_some() {
self.make_mut(schema).ok()
} else {
None
}
}
pub(crate) fn remove(&self, schema: &mut FederationSchema) -> Result<(), FederationError> {
let Some(argument) = self.try_get(&schema.schema) else {
return Ok(());
};
self.remove_references(argument, &mut schema.referencers)?;
self.parent()
.make_mut(&mut schema.schema)?
.make_mut()
.arguments
.retain(|other_argument| other_argument.name != self.argument_name);
Ok(())
}
pub(crate) fn insert_directive(
&self,
schema: &mut FederationSchema,
directive: Node<Directive>,
) -> Result<(), FederationError> {
let argument = self.make_mut(&mut schema.schema)?;
if argument
.directives
.iter()
.any(|other_directive| other_directive.ptr_eq(&directive))
{
return Err(SingleFederationError::Internal {
message: format!(
"Directive application \"@{}\" already exists on directive argument \"{}\"",
directive.name, self,
),
}
.into());
}
let name = directive.name.clone();
argument.make_mut().directives.push(directive);
self.insert_directive_name_references(&mut schema.referencers, &name)
}
pub(crate) fn remove_directive_name(&self, schema: &mut FederationSchema, name: &str) {
let Some(argument) = self.try_make_mut(&mut schema.schema) else {
return;
};
self.remove_directive_name_references(&mut schema.referencers, name);
argument
.make_mut()
.directives
.retain(|other_directive| other_directive.name != name);
}
fn insert(
&self,
schema: &mut FederationSchema,
argument: Node<InputValueDefinition>,
) -> Result<(), FederationError> {
let parent = self.parent().make_mut(&mut schema.schema)?;
if parent.argument_by_name(&argument.name).is_some() {
bail!(r#"Directive argument "{self}" already exists in schema"#);
}
self.insert_references(&argument, &mut schema.referencers)?;
parent.make_mut().arguments.push(argument);
Ok(())
}
fn insert_references(
&self,
argument: &Node<InputValueDefinition>,
referencers: &mut Referencers,
) -> Result<(), FederationError> {
if is_graphql_reserved_name(&self.argument_name) {
bail!(r#"Cannot insert reserved directive argument "{self}""#);
}
validate_node_directives(argument.directives.deref())?;
for directive_reference in argument.directives.iter() {
self.insert_directive_name_references(referencers, &directive_reference.name)?;
}
self.insert_type_references(argument, referencers)
}
fn remove_references(
&self,
argument: &Node<InputValueDefinition>,
referencers: &mut Referencers,
) -> Result<(), FederationError> {
if is_graphql_reserved_name(&self.argument_name) {
bail!(r#"Cannot remove reserved directive argument "{self}""#);
}
for directive_reference in argument.directives.iter() {
self.remove_directive_name_references(referencers, &directive_reference.name);
}
self.remove_type_references(argument, referencers);
Ok(())
}
fn insert_directive_name_references(
&self,
referencers: &mut Referencers,
name: &Name,
) -> Result<(), FederationError> {
let directive_referencers = referencers.directives.get_mut(name).ok_or_else(|| {
SingleFederationError::Internal {
message: format!(
"Directive argument \"{self}\"'s directive application \"@{name}\" does not refer to an existing directive.",
),
}
})?;
directive_referencers
.directive_arguments
.insert(self.clone());
Ok(())
}
fn remove_directive_name_references(&self, referencers: &mut Referencers, name: &str) {
let Some(directive_referencers) = referencers.directives.get_mut(name) else {
return;
};
directive_referencers.directive_arguments.shift_remove(self);
}
fn insert_type_references(
&self,
argument: &Node<InputValueDefinition>,
referencers: &mut Referencers,
) -> Result<(), FederationError> {
let input_type_reference = argument.ty.inner_named_type();
if let Some(scalar_type_referencers) =
referencers.scalar_types.get_mut(input_type_reference)
{
scalar_type_referencers
.directive_arguments
.insert(self.clone());
} else if let Some(enum_type_referencers) =
referencers.enum_types.get_mut(input_type_reference)
{
enum_type_referencers
.directive_arguments
.insert(self.clone());
} else if let Some(input_object_type_referencers) =
referencers.input_object_types.get_mut(input_type_reference)
{
input_object_type_referencers
.directive_arguments
.insert(self.clone());
} else {
return Err(FederationError::internal(format!(
"Directive argument \"{}\"'s inner type \"{}\" does not refer to an existing input type.",
self,
input_type_reference.deref(),
)));
}
Ok(())
}
fn remove_type_references(
&self,
argument: &Node<InputValueDefinition>,
referencers: &mut Referencers,
) {
let input_type_reference = argument.ty.inner_named_type();
if let Some(scalar_type_referencers) =
referencers.scalar_types.get_mut(input_type_reference)
{
scalar_type_referencers
.directive_arguments
.shift_remove(self);
} else if let Some(enum_type_referencers) =
referencers.enum_types.get_mut(input_type_reference)
{
enum_type_referencers.directive_arguments.shift_remove(self);
} else if let Some(input_object_type_referencers) =
referencers.input_object_types.get_mut(input_type_reference)
{
input_object_type_referencers
.directive_arguments
.shift_remove(self);
}
}
fn rename_type(
&self,
schema: &mut FederationSchema,
new_name: Name,
) -> Result<(), FederationError> {
let input_value_definition = self.make_mut(&mut schema.schema)?.make_mut();
rename_type(input_value_definition.ty.make_mut(), new_name);
Ok(())
}
}
impl Display for DirectiveArgumentDefinitionPosition {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "@{}({}:)", self.directive_name, self.argument_name)
}
}
impl HasDefaultValue for DirectiveArgumentDefinitionPosition {
fn is_input_field() -> bool {
false
}
fn get_default_value<'schema>(
&self,
schema: &'schema FederationSchema,
) -> Option<&'schema Node<ast::Value>> {
self.try_get(&schema.schema)
.and_then(|arg| arg.default_value.as_ref())
}
fn set_default_value(
&self,
schema: &mut FederationSchema,
default: Option<Node<ast::Value>>,
) -> Result<(), FederationError> {
let arg = self.make_mut(&mut schema.schema)?;
arg.make_mut().default_value = default;
Ok(())
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, derive_more::Display)]
pub(crate) enum DirectiveTargetPosition {
Schema(SchemaDefinitionPosition),
ScalarType(ScalarTypeDefinitionPosition),
ObjectType(ObjectTypeDefinitionPosition),
ObjectField(ObjectFieldDefinitionPosition),
ObjectFieldArgument(ObjectFieldArgumentDefinitionPosition),
InterfaceType(InterfaceTypeDefinitionPosition),
InterfaceField(InterfaceFieldDefinitionPosition),
InterfaceFieldArgument(InterfaceFieldArgumentDefinitionPosition),
UnionType(UnionTypeDefinitionPosition),
EnumType(EnumTypeDefinitionPosition),
EnumValue(EnumValueDefinitionPosition),
InputObjectType(InputObjectTypeDefinitionPosition),
InputObjectField(InputObjectFieldDefinitionPosition),
DirectiveArgument(DirectiveArgumentDefinitionPosition),
}
impl DirectiveTargetPosition {
pub(crate) fn exists_in(&self, schema: &FederationSchema) -> bool {
match self {
Self::Schema(_) => true,
Self::ScalarType(pos) => pos.try_get(&schema.schema).is_some(),
Self::ObjectType(pos) => pos.try_get(&schema.schema).is_some(),
Self::ObjectField(pos) => pos.try_get(&schema.schema).is_some(),
Self::ObjectFieldArgument(pos) => pos.try_get(&schema.schema).is_some(),
Self::InterfaceType(pos) => pos.try_get(&schema.schema).is_some(),
Self::InterfaceField(pos) => pos.try_get(&schema.schema).is_some(),
Self::InterfaceFieldArgument(pos) => pos.try_get(&schema.schema).is_some(),
Self::UnionType(pos) => pos.try_get(&schema.schema).is_some(),
Self::EnumType(pos) => pos.try_get(&schema.schema).is_some(),
Self::EnumValue(pos) => pos.try_get(&schema.schema).is_some(),
Self::InputObjectType(pos) => pos.try_get(&schema.schema).is_some(),
Self::InputObjectField(pos) => pos.try_get(&schema.schema).is_some(),
Self::DirectiveArgument(pos) => pos.try_get(&schema.schema).is_some(),
}
}
pub(crate) fn get_all_applied_directives<'schema>(
&self,
schema: &'schema FederationSchema,
) -> Vec<&'schema Node<Directive>> {
match self {
Self::Schema(pos) => pos
.get_all_applied_directives(schema)
.map(|component| &component.node)
.collect(),
Self::ScalarType(pos) => pos
.get_all_applied_directives(schema)
.map(|it| it.map(|component| &component.node).collect())
.unwrap_or_default(),
Self::ObjectType(pos) => pos
.get_all_applied_directives(schema)
.map(|it| it.map(|component| &component.node).collect())
.unwrap_or_default(),
Self::ObjectField(pos) => pos
.get_all_applied_directives(schema)
.map(|it| it.collect())
.unwrap_or_default(),
Self::ObjectFieldArgument(pos) => pos
.get_all_applied_directives(schema)
.map(|it| it.collect())
.unwrap_or_default(),
Self::InterfaceType(pos) => pos
.get_all_applied_directives(schema)
.map(|it| it.map(|component| &component.node).collect())
.unwrap_or_default(),
Self::InterfaceField(pos) => pos
.get_all_applied_directives(schema)
.map(|it| it.collect())
.unwrap_or_default(),
Self::InterfaceFieldArgument(pos) => pos
.get_all_applied_directives(schema)
.map(|it| it.collect())
.unwrap_or_default(),
Self::UnionType(pos) => pos
.get_all_applied_directives(schema)
.map(|it| it.map(|component| &component.node).collect())
.unwrap_or_default(),
Self::EnumType(pos) => pos
.get_all_applied_directives(schema)
.map(|it| it.map(|component| &component.node).collect())
.unwrap_or_default(),
Self::EnumValue(pos) => pos
.get_all_applied_directives(schema)
.map(|it| it.collect())
.unwrap_or_default(),
Self::InputObjectType(pos) => pos
.get_all_applied_directives(schema)
.map(|it| it.map(|component| &component.node).collect())
.unwrap_or_default(),
Self::InputObjectField(pos) => pos
.get_all_applied_directives(schema)
.map(|it| it.collect())
.unwrap_or_default(),
Self::DirectiveArgument(pos) => pos
.get_all_applied_directives(schema)
.map(|it| it.collect())
.unwrap_or_default(),
}
}
pub(crate) fn insert_directive(
&self,
schema: &mut FederationSchema,
directive: Directive,
) -> Result<(), FederationError> {
match self {
Self::Schema(pos) => pos.insert_directive(schema, Component::new(directive)),
Self::ScalarType(pos) => pos.insert_directive(schema, Component::new(directive)),
Self::ObjectType(pos) => pos.insert_directive(schema, Component::new(directive)),
Self::ObjectField(pos) => pos.insert_directive(schema, Node::new(directive)),
Self::ObjectFieldArgument(pos) => pos.insert_directive(schema, Node::new(directive)),
Self::InterfaceType(pos) => pos.insert_directive(schema, Component::new(directive)),
Self::InterfaceField(pos) => pos.insert_directive(schema, Node::new(directive)),
Self::InterfaceFieldArgument(pos) => pos.insert_directive(schema, Node::new(directive)),
Self::UnionType(pos) => pos.insert_directive(schema, Component::new(directive)),
Self::EnumType(pos) => pos.insert_directive(schema, Component::new(directive)),
Self::EnumValue(pos) => pos.insert_directive(schema, Node::new(directive)),
Self::InputObjectType(pos) => pos.insert_directive(schema, Component::new(directive)),
Self::InputObjectField(pos) => pos.insert_directive(schema, Node::new(directive)),
Self::DirectiveArgument(pos) => pos.insert_directive(schema, Node::new(directive)),
}
}
}
impl From<DirectiveArgumentDefinitionPosition> for DirectiveTargetPosition {
fn from(pos: DirectiveArgumentDefinitionPosition) -> Self {
DirectiveTargetPosition::DirectiveArgument(pos)
}
}
impl From<EnumValueDefinitionPosition> for DirectiveTargetPosition {
fn from(pos: EnumValueDefinitionPosition) -> Self {
DirectiveTargetPosition::EnumValue(pos)
}
}
impl From<FieldArgumentDefinitionPosition> for DirectiveTargetPosition {
fn from(pos: FieldArgumentDefinitionPosition) -> Self {
match pos {
FieldArgumentDefinitionPosition::Object(pos) => {
DirectiveTargetPosition::ObjectFieldArgument(pos)
}
FieldArgumentDefinitionPosition::Interface(pos) => {
DirectiveTargetPosition::InterfaceFieldArgument(pos)
}
}
}
}
impl From<InputObjectFieldDefinitionPosition> for DirectiveTargetPosition {
fn from(pos: InputObjectFieldDefinitionPosition) -> Self {
DirectiveTargetPosition::InputObjectField(pos)
}
}
impl From<ObjectOrInterfaceFieldDefinitionPosition> for DirectiveTargetPosition {
fn from(pos: ObjectOrInterfaceFieldDefinitionPosition) -> Self {
match pos {
ObjectOrInterfaceFieldDefinitionPosition::Object(pos) => {
DirectiveTargetPosition::ObjectField(pos)
}
ObjectOrInterfaceFieldDefinitionPosition::Interface(pos) => {
DirectiveTargetPosition::InterfaceField(pos)
}
}
}
}
impl From<ObjectFieldDefinitionPosition> for DirectiveTargetPosition {
fn from(pos: ObjectFieldDefinitionPosition) -> Self {
DirectiveTargetPosition::ObjectField(pos)
}
}
impl From<ObjectOrInterfaceTypeDefinitionPosition> for DirectiveTargetPosition {
fn from(pos: ObjectOrInterfaceTypeDefinitionPosition) -> Self {
match pos {
ObjectOrInterfaceTypeDefinitionPosition::Object(pos) => {
DirectiveTargetPosition::ObjectType(pos)
}
ObjectOrInterfaceTypeDefinitionPosition::Interface(pos) => {
DirectiveTargetPosition::InterfaceType(pos)
}
}
}
}
impl From<ObjectTypeDefinitionPosition> for DirectiveTargetPosition {
fn from(pos: ObjectTypeDefinitionPosition) -> Self {
DirectiveTargetPosition::ObjectType(pos)
}
}
impl From<SchemaDefinitionPosition> for DirectiveTargetPosition {
fn from(pos: SchemaDefinitionPosition) -> Self {
DirectiveTargetPosition::Schema(pos)
}
}
impl From<TypeDefinitionPosition> for DirectiveTargetPosition {
fn from(pos: TypeDefinitionPosition) -> Self {
match pos {
TypeDefinitionPosition::Scalar(scalar) => Self::ScalarType(scalar),
TypeDefinitionPosition::Object(object) => Self::ObjectType(object),
TypeDefinitionPosition::Interface(itf) => Self::InterfaceType(itf),
TypeDefinitionPosition::Union(union) => Self::UnionType(union),
TypeDefinitionPosition::Enum(enm) => Self::EnumType(enm),
TypeDefinitionPosition::InputObject(input_object) => {
Self::InputObjectType(input_object)
}
}
}
}
impl TryFrom<FieldDefinitionPosition> for DirectiveTargetPosition {
type Error = PositionConvertError<FieldDefinitionPosition>;
fn try_from(value: FieldDefinitionPosition) -> Result<Self, Self::Error> {
match value {
FieldDefinitionPosition::Object(obj_field) => Ok(Self::ObjectField(obj_field)),
FieldDefinitionPosition::Interface(itf_field) => Ok(Self::InterfaceField(itf_field)),
FieldDefinitionPosition::Union(_) => Err(PositionConvertError {
actual: value,
expected: "DirectiveTargetPosition",
}),
}
}
}
pub(crate) fn is_graphql_reserved_name(name: &str) -> bool {
name.starts_with("__")
}
pub(crate) static INTROSPECTION_TYPENAME_FIELD_NAME: Name = name!("__typename");
fn validate_component_directives(
directives: &[Component<Directive>],
) -> Result<(), FederationError> {
for directive in directives.iter() {
if directives
.iter()
.filter(|other_directive| other_directive.ptr_eq(directive))
.count()
> 1
{
return Err(SingleFederationError::Internal {
message: format!(
"Directive application \"@{}\" is duplicated on schema element",
directive.name,
),
}
.into());
}
}
Ok(())
}
fn validate_node_directives(directives: &[Node<Directive>]) -> Result<(), FederationError> {
for directive in directives.iter() {
if directives
.iter()
.filter(|other_directive| other_directive.ptr_eq(directive))
.count()
> 1
{
return Err(SingleFederationError::Internal {
message: format!(
"Directive application \"@{}\" is duplicated on schema element",
directive.name,
),
}
.into());
}
}
Ok(())
}
fn validate_arguments(arguments: &[Node<InputValueDefinition>]) -> Result<(), FederationError> {
for argument in arguments.iter() {
if arguments
.iter()
.filter(|other_argument| other_argument.name == argument.name)
.count()
> 1
{
return Err(SingleFederationError::Internal {
message: format!(
"Argument \"{}\" is duplicated on schema element",
argument.name,
),
}
.into());
}
}
Ok(())
}
fn rename_type(ast_type: &mut ast::Type, new_name: Name) {
match ast_type {
ast::Type::Named(name) => *name = new_name,
ast::Type::NonNullNamed(name) => *name = new_name,
ast::Type::List(boxed) => rename_type(boxed, new_name),
ast::Type::NonNullList(boxed) => rename_type(boxed, new_name),
}
}
impl FederationSchema {
pub(crate) fn new(schema: Schema) -> Result<Self, FederationError> {
let mut schema = Self::new_uninitialized(schema)?;
schema.collect_links_metadata()?;
schema.collect_shallow_references();
schema.collect_deep_references()?;
Ok(schema)
}
pub(crate) fn new_uninitialized(schema: Schema) -> Result<FederationSchema, FederationError> {
Ok(Self {
schema,
referencers: Default::default(),
links_metadata: None,
subgraph_metadata: None,
})
}
pub(crate) fn collect_links_metadata(&mut self) -> Result<(), FederationError> {
self.links_metadata = links_metadata(self.schema())?.map(Box::new);
Ok(())
}
pub(crate) fn collect_shallow_references(&mut self) {
for (type_name, type_) in self.schema.types.iter() {
match type_ {
ExtendedType::Scalar(_) => {
self.referencers
.scalar_types
.insert(type_name.clone(), Default::default());
}
ExtendedType::Object(_) => {
self.referencers
.object_types
.insert(type_name.clone(), Default::default());
}
ExtendedType::Interface(_) => {
self.referencers
.interface_types
.insert(type_name.clone(), Default::default());
}
ExtendedType::Union(_) => {
self.referencers
.union_types
.insert(type_name.clone(), Default::default());
}
ExtendedType::Enum(_) => {
self.referencers
.enum_types
.insert(type_name.clone(), Default::default());
}
ExtendedType::InputObject(_) => {
self.referencers
.input_object_types
.insert(type_name.clone(), Default::default());
}
}
}
for directive_name in self.schema.directive_definitions.keys() {
self.referencers
.directives
.insert(directive_name.clone(), Default::default());
}
}
pub(crate) fn collect_deep_references(&mut self) -> Result<(), FederationError> {
SchemaDefinitionPosition.insert_references(
&self.schema.schema_definition,
&self.schema,
&mut self.referencers,
)?;
for (type_name, type_) in self.schema.types.iter() {
match type_ {
ExtendedType::Scalar(type_) => {
ScalarTypeDefinitionPosition {
type_name: type_name.clone(),
}
.insert_references(type_, &mut self.referencers)?;
}
ExtendedType::Object(type_) => {
ObjectTypeDefinitionPosition {
type_name: type_name.clone(),
}
.insert_references(
type_,
&self.schema,
&mut self.referencers,
)?;
}
ExtendedType::Interface(type_) => {
InterfaceTypeDefinitionPosition {
type_name: type_name.clone(),
}
.insert_references(
type_,
&self.schema,
&mut self.referencers,
)?;
}
ExtendedType::Union(type_) => {
UnionTypeDefinitionPosition {
type_name: type_name.clone(),
}
.insert_references(type_, &mut self.referencers)?;
}
ExtendedType::Enum(type_) => {
EnumTypeDefinitionPosition {
type_name: type_name.clone(),
}
.insert_references(type_, &mut self.referencers)?;
}
ExtendedType::InputObject(type_) => {
InputObjectTypeDefinitionPosition {
type_name: type_name.clone(),
}
.insert_references(type_, &mut self.referencers)?;
}
}
}
for (directive_name, directive) in self.schema.directive_definitions.iter() {
DirectiveDefinitionPosition {
directive_name: directive_name.clone(),
}
.insert_references(directive, &mut self.referencers)?;
}
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn remove_type_recursive() {
let schema = r#"
type User {
id: ID!
profile: UserProfile
}
type UserProfile {
username: String!
}
type Query {
me: User
}
"#;
let mut schema = FederationSchema::new(
Schema::parse_and_validate(schema, "schema.graphql")
.unwrap()
.into_inner(),
)
.unwrap();
let position = ObjectTypeDefinitionPosition::new(name!("UserProfile"));
position.remove_recursive(&mut schema).unwrap();
insta::assert_snapshot!(schema.schema(), @r#"
type User {
id: ID!
}
type Query {
me: User
}
"#);
}
#[test]
fn remove_interface_recursive() {
let schema = r#"
type User {
id: ID!
profile: UserProfile
}
interface UserProfile {
username: String!
}
type Query {
me: User
}
"#;
let mut schema = FederationSchema::new(
Schema::parse_and_validate(schema, "schema.graphql")
.unwrap()
.into_inner(),
)
.unwrap();
let position = InterfaceTypeDefinitionPosition {
type_name: name!("UserProfile"),
};
position.remove_recursive(&mut schema).unwrap();
insta::assert_snapshot!(schema.schema(), @r#"
type User {
id: ID!
}
type Query {
me: User
}
"#);
}
#[test]
fn rename_type_updates_all_referencers() {
let schema = Schema::parse_and_validate(
r#"
type Query {
user: User
users(filter: UserFilter): [User!]!
}
type User {
id: ID!
name: String!
posts: [Post!]!
}
type Post {
id: ID!
author: User!
coAuthor: User
}
input UserFilter {
name: String
}
"#,
"test-schema.graphqls",
)
.unwrap();
let mut schema = FederationSchema::new(schema.into_inner()).unwrap();
let user_position =
TypeDefinitionPosition::Object(ObjectTypeDefinitionPosition::new(name!("User")));
user_position.rename(&mut schema, name!("Person")).unwrap();
let referencers = schema.referencers();
assert!(
referencers.object_types.get("User").is_none(),
"Referencers should not have old type name 'User' as a key after rename"
);
assert!(
referencers.object_types.get("Person").is_some(),
"Referencers should have new type name 'Person' as a key after rename"
);
let string_refs = referencers.scalar_types.get("String").unwrap();
assert!(
string_refs
.object_fields
.iter()
.any(|f| f.type_name == name!("Person") && f.field_name == name!("name")),
"String referencers should contain Person.name field"
);
assert!(
!string_refs
.object_fields
.iter()
.any(|f| f.type_name == name!("User")),
"String referencers should not contain any fields on User type"
);
let id_refs = referencers.scalar_types.get("ID").unwrap();
assert!(
id_refs
.object_fields
.iter()
.any(|f| f.type_name == name!("Person") && f.field_name == name!("id")),
"ID referencers should contain Person.id field"
);
assert!(
!id_refs
.object_fields
.iter()
.any(|f| f.type_name == name!("User")),
"ID referencers should not contain any fields on User type"
);
let post_refs = referencers.object_types.get("Post").unwrap();
assert!(
post_refs
.object_fields
.iter()
.any(|f| f.type_name == name!("Person") && f.field_name == name!("posts")),
"Post referencers should contain Person.posts field"
);
assert!(
!post_refs
.object_fields
.iter()
.any(|f| f.type_name == name!("User")),
"Post referencers should not contain any fields on User type"
);
let person_refs = referencers.object_types.get("Person").unwrap();
assert!(
person_refs
.object_fields
.iter()
.any(|f| f.type_name == name!("Query") && f.field_name == name!("user")),
"Person referencers should contain Query.user field"
);
assert!(
person_refs
.object_fields
.iter()
.any(|f| f.type_name == name!("Post") && f.field_name == name!("author")),
"Person referencers should contain Post.author field"
);
assert!(
person_refs
.object_fields
.iter()
.any(|f| f.type_name == name!("Post") && f.field_name == name!("coAuthor")),
"Person referencers should contain Post.coAuthor field"
);
}
#[test]
fn rename_type() {
let schema = Schema::parse_and_validate(
r#"
schema {
query: MyQuery
}
type MyQuery {
a: MyData
}
interface OtherInterface {
b: MyValue
}
interface IMyData implements OtherInterface {
b: MyValue
}
type MyData implements IMyData & OtherInterface {
b: MyValue
c: String
}
type OtherData {
d: String
e: MyAorB
}
union MyUnionData = MyData | OtherData
scalar MyValue
enum MyAorB {
A
B
}
"#,
"test-schema.graphqls",
)
.unwrap();
let mut schema = FederationSchema::new(schema.into_inner()).unwrap();
let query_position = ObjectTypeDefinitionPosition::new(name!("MyQuery"));
let interface_position = InterfaceTypeDefinitionPosition {
type_name: name!("IMyData"),
};
let data_position = ObjectTypeDefinitionPosition::new(name!("MyData"));
let scalar_position = ScalarTypeDefinitionPosition {
type_name: name!("MyValue"),
};
let union_position = UnionTypeDefinitionPosition {
type_name: name!("MyUnionData"),
};
let enum_position = EnumTypeDefinitionPosition {
type_name: name!("MyAorB"),
};
query_position.rename(&mut schema, name!("Query")).unwrap();
interface_position
.rename(&mut schema, name!("IData"))
.unwrap();
data_position.rename(&mut schema, name!("Data")).unwrap();
scalar_position.rename(&mut schema, name!("Value")).unwrap();
union_position
.rename(&mut schema, name!("UnionData"))
.unwrap();
enum_position.rename(&mut schema, name!("AorB")).unwrap();
insta::assert_snapshot!(schema.schema(), @r#"
schema {
query: Query
}
type Query {
a: Data
}
interface OtherInterface {
b: Value
}
interface IData implements OtherInterface {
b: Value
}
type Data implements OtherInterface & IData {
b: Value
c: String
}
type OtherData {
d: String
e: AorB
}
union UnionData = OtherData | Data
scalar Value
enum AorB {
A
B
}
"#);
}
}