use std::{borrow::Cow, marker::PhantomData};
use super::{SchemaError, names::FieldName};
use cynic_parser::type_system as parser;
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(
feature = "rkyv",
derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)
)]
pub struct SchemaRoots<'a> {
pub query: ObjectType<'a>,
pub mutation: Option<ObjectType<'a>>,
pub subscription: Option<ObjectType<'a>>,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(
feature = "rkyv",
derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)
)]
pub enum Type<'a> {
Scalar(ScalarType<'a>),
Object(ObjectType<'a>),
Interface(InterfaceType<'a>),
Union(UnionType<'a>),
Enum(EnumType<'a>),
InputObject(InputObjectType<'a>),
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(
feature = "rkyv",
derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)
)]
pub enum InputType<'a> {
Scalar(ScalarType<'a>),
Enum(EnumType<'a>),
InputObject(InputObjectType<'a>),
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(
feature = "rkyv",
derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)
)]
pub enum OutputType<'a> {
Scalar(ScalarType<'a>),
Object(ObjectType<'a>),
Interface(InterfaceType<'a>),
Union(UnionType<'a>),
Enum(EnumType<'a>),
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(
feature = "rkyv",
derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)
)]
pub struct ScalarType<'a> {
#[cfg_attr(feature = "rkyv", rkyv(with = rkyv::with::AsOwned))]
pub name: Cow<'a, str>,
pub builtin: bool,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(
feature = "rkyv",
derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)
)]
pub struct ObjectType<'a> {
#[cfg_attr(feature = "rkyv", rkyv(with = rkyv::with::AsOwned))]
pub name: Cow<'a, str>,
pub implements_interfaces: Vec<InterfaceRef<'a>>,
pub fields: Vec<Field<'a>>,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(
feature = "rkyv",
derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)
)]
pub struct Field<'a> {
pub name: FieldName<'a>,
pub arguments: Vec<InputValue<'a>>,
pub field_type: TypeRef<'a, OutputType<'a>>,
#[cfg_attr(feature = "rkyv", rkyv(with = rkyv::with::AsOwned))]
pub(super) parent_type_name: Cow<'a, str>,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(
feature = "rkyv",
derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)
)]
pub struct InputValue<'a> {
pub name: FieldName<'a>,
pub value_type: TypeRef<'a, InputType<'a>>,
pub has_default: bool,
}
impl InputValue<'_> {
pub fn is_nullable(&self) -> bool {
matches!(self.value_type, TypeRef::Nullable(_))
}
pub fn is_required(&self) -> bool {
!(self.has_default || matches!(self.value_type, TypeRef::Nullable(_)))
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(
feature = "rkyv",
derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)
)]
pub struct InterfaceType<'a> {
#[cfg_attr(feature = "rkyv", rkyv(with = rkyv::with::AsOwned))]
pub name: Cow<'a, str>,
pub fields: Vec<Field<'a>>,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(
feature = "rkyv",
derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)
)]
pub struct UnionType<'a> {
#[cfg_attr(feature = "rkyv", rkyv(with = rkyv::with::AsOwned))]
pub name: Cow<'a, str>,
pub types: Vec<ObjectRef<'a>>,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(
feature = "rkyv",
derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)
)]
pub struct EnumType<'a> {
#[cfg_attr(feature = "rkyv", rkyv(with = rkyv::with::AsOwned))]
pub name: Cow<'a, str>,
pub values: Vec<EnumValue<'a>>,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(
feature = "rkyv",
derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)
)]
pub struct EnumValue<'a> {
pub name: FieldName<'a>,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(
feature = "rkyv",
derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)
)]
pub struct InputObjectType<'a> {
#[cfg_attr(feature = "rkyv", rkyv(with = rkyv::with::AsOwned))]
pub name: Cow<'a, str>,
pub fields: Vec<InputValue<'a>>,
pub is_one_of: bool,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(
feature = "rkyv",
derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)
)]
pub struct Directive<'a> {
#[cfg_attr(feature = "rkyv", rkyv(with = rkyv::with::AsOwned))]
pub name: Cow<'a, str>,
pub arguments: Vec<InputValue<'a>>,
pub locations: Vec<DirectiveLocation>,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(
feature = "rkyv",
derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)
)]
pub enum DirectiveLocation {
Query,
Mutation,
Subscription,
Field,
FragmentDefinition,
FragmentSpread,
InlineFragment,
Schema,
Scalar,
Object,
FieldDefinition,
ArgumentDefinition,
Interface,
Union,
Enum,
EnumValue,
InputObject,
InputFieldDefinition,
VariableDefinition,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(
feature = "rkyv",
derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)
)]
pub enum Kind {
InputType,
OutputType,
Object,
Scalar,
Interface,
Union,
Enum,
InputObject,
ObjectOrInterface,
UnionOrInterface,
}
impl Kind {
pub fn of_definition(definition: parser::TypeDefinition<'_>) -> Self {
match definition {
parser::TypeDefinition::Scalar(_) => Kind::Scalar,
parser::TypeDefinition::Object(_) => Kind::Object,
parser::TypeDefinition::Interface(_) => Kind::Interface,
parser::TypeDefinition::Union(_) => Kind::Union,
parser::TypeDefinition::Enum(_) => Kind::Enum,
parser::TypeDefinition::InputObject(_) => Kind::InputObject,
}
}
}
impl<'a> Type<'a> {
pub fn object(&self) -> Option<&ObjectType<'a>> {
match self {
Type::Object(obj) => Some(obj),
_ => None,
}
}
}
impl<'a> ObjectType<'a> {
pub fn field<N>(&self, name: &N) -> Option<&Field<'a>>
where
N: ?Sized,
for<'b> FieldName<'b>: PartialEq<N>,
{
self.fields.iter().find(|field| field.name == *name)
}
}
impl<'a> InputObjectType<'a> {
pub fn field<N>(&self, name: &N) -> Option<&InputValue<'a>>
where
for<'b> FieldName<'b>: PartialEq<N>,
{
self.fields.iter().find(|field| field.name == *name)
}
}
impl<'a> EnumType<'a> {
pub fn value<N>(&self, name: &N) -> Option<&EnumValue<'a>>
where
N: ?Sized,
for<'b> FieldName<'b>: PartialEq<N>,
{
self.values.iter().find(|value| value.name == *name)
}
}
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(
feature = "rkyv",
derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)
)]
pub struct ObjectRef<'a>(
#[cfg_attr(feature = "rkyv", rkyv(with = rkyv::with::AsOwned))] pub(super) Cow<'a, str>,
);
impl std::fmt::Display for ObjectRef<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.0)
}
}
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(
feature = "rkyv",
derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)
)]
pub struct InterfaceRef<'a>(
#[cfg_attr(feature = "rkyv", rkyv(with = rkyv::with::AsOwned))] pub(super) Cow<'a, str>,
);
#[derive(Clone, PartialEq, Eq, Hash)]
#[cfg_attr(
feature = "rkyv",
derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize),
rkyv(
serialize_bounds(__S: rkyv::ser::Writer),
deserialize_bounds(__D::Error: rkyv::rancor::Source),
bytecheck(
bounds ( __C: rkyv::validation::ArchiveContext )
)
)
)]
pub enum TypeRef<'a, T> {
Named(
#[cfg_attr(feature = "rkyv", rkyv(with = rkyv::with::AsOwned))] Cow<'a, str>,
PhantomData<fn() -> T>,
),
List(#[cfg_attr(feature = "rkyv", rkyv(omit_bounds))] Box<TypeRef<'a, T>>),
Nullable(#[cfg_attr(feature = "rkyv", rkyv(omit_bounds))] Box<TypeRef<'a, T>>),
}
impl<'a, T> TypeRef<'a, T>
where
Type<'a>: TryInto<T>,
<Type<'a> as TryInto<T>>::Error: std::fmt::Debug,
{
pub fn inner_type<SchemaState>(&self, schema: &'a super::Schema<'a, SchemaState>) -> T {
match self {
TypeRef::Named(name, _) => {
schema.type_index.unsafe_lookup(name).try_into().unwrap()
}
TypeRef::List(inner) => inner.inner_type(schema),
TypeRef::Nullable(inner) => inner.inner_type(schema),
}
}
}
impl std::fmt::Debug for TypeRef<'_, InputType<'_>> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Named(arg0, _) => f.debug_tuple("NamedInputType").field(arg0).finish(),
Self::List(arg0) => f.debug_tuple("ListType").field(arg0).finish(),
Self::Nullable(arg0) => f.debug_tuple("NullableType").field(arg0).finish(),
}
}
}
impl std::fmt::Debug for TypeRef<'_, OutputType<'_>> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Named(arg0, _) => f.debug_tuple("NamedOutputType").field(arg0).finish(),
Self::List(arg0) => f.debug_tuple("ListType").field(arg0).finish(),
Self::Nullable(arg0) => f.debug_tuple("NullableType").field(arg0).finish(),
}
}
}
impl<'a> TryFrom<Type<'a>> for OutputType<'a> {
type Error = SchemaError;
fn try_from(value: Type<'a>) -> Result<Self, Self::Error> {
match value {
Type::Scalar(inner) => Ok(OutputType::Scalar(inner)),
Type::Object(inner) => Ok(OutputType::Object(inner)),
Type::Interface(inner) => Ok(OutputType::Interface(inner)),
Type::Union(inner) => Ok(OutputType::Union(inner)),
Type::Enum(inner) => Ok(OutputType::Enum(inner)),
Type::InputObject(inner) => Err(SchemaError::UnexpectedKind {
name: inner.name.to_string(),
expected: Kind::OutputType,
found: Kind::InputObject,
}),
}
}
}
impl<'a> TryFrom<Type<'a>> for InputType<'a> {
type Error = SchemaError;
fn try_from(value: Type<'a>) -> Result<Self, Self::Error> {
match value {
Type::Scalar(inner) => Ok(InputType::Scalar(inner)),
Type::InputObject(inner) => Ok(InputType::InputObject(inner)),
Type::Object(inner) => Err(SchemaError::UnexpectedKind {
name: inner.name.to_string(),
expected: Kind::InputType,
found: Kind::Object,
}),
Type::Enum(inner) => Ok(InputType::Enum(inner)),
_ => Err(SchemaError::unexpected_kind(value, Kind::InputType)),
}
}
}
impl<'a> TryFrom<Type<'a>> for ObjectType<'a> {
type Error = SchemaError;
fn try_from(value: Type<'a>) -> Result<Self, Self::Error> {
match value {
Type::Object(inner) => Ok(inner),
_ => Err(SchemaError::unexpected_kind(value, Kind::Object)),
}
}
}
impl<'a> TryFrom<Type<'a>> for InputObjectType<'a> {
type Error = SchemaError;
fn try_from(value: Type<'a>) -> Result<Self, Self::Error> {
match value {
Type::InputObject(inner) => Ok(inner),
_ => Err(SchemaError::unexpected_kind(value, Kind::InputObject)),
}
}
}
impl<'a> TryFrom<Type<'a>> for EnumType<'a> {
type Error = SchemaError;
fn try_from(value: Type<'a>) -> Result<Self, Self::Error> {
match value {
Type::Enum(inner) => Ok(inner),
_ => Err(SchemaError::unexpected_kind(value, Kind::Enum)),
}
}
}
impl<'a> Type<'a> {
pub fn name(&'a self) -> &'a str {
match self {
Type::Scalar(inner) => inner.name.as_ref(),
Type::Object(inner) => inner.name.as_ref(),
Type::Interface(inner) => inner.name.as_ref(),
Type::Union(inner) => inner.name.as_ref(),
Type::Enum(inner) => inner.name.as_ref(),
Type::InputObject(inner) => inner.name.as_ref(),
}
}
pub fn kind(&self) -> Kind {
match self {
Type::Scalar(_) => Kind::Scalar,
Type::Object(_) => Kind::Object,
Type::Interface(_) => Kind::Interface,
Type::Union(_) => Kind::Union,
Type::Enum(_) => Kind::Enum,
Type::InputObject(_) => Kind::InputObject,
}
}
}
impl std::fmt::Display for Kind {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let s = match self {
Kind::InputType => "input type",
Kind::OutputType => "output type",
Kind::Object => "object",
Kind::Scalar => "scalar",
Kind::Interface => "interface",
Kind::Union => "union",
Kind::Enum => "enum",
Kind::InputObject => "input object",
Kind::ObjectOrInterface => "object or interface",
Kind::UnionOrInterface => "union or interface",
};
write!(f, "{}", s)
}
}
impl From<parser::DirectiveLocation> for DirectiveLocation {
fn from(value: parser::DirectiveLocation) -> Self {
match value {
parser::DirectiveLocation::Query => DirectiveLocation::Query,
parser::DirectiveLocation::Mutation => DirectiveLocation::Mutation,
parser::DirectiveLocation::Subscription => DirectiveLocation::Subscription,
parser::DirectiveLocation::Field => DirectiveLocation::Field,
parser::DirectiveLocation::FragmentDefinition => DirectiveLocation::FragmentDefinition,
parser::DirectiveLocation::FragmentSpread => DirectiveLocation::FragmentSpread,
parser::DirectiveLocation::InlineFragment => DirectiveLocation::InlineFragment,
parser::DirectiveLocation::Schema => DirectiveLocation::Schema,
parser::DirectiveLocation::Scalar => DirectiveLocation::Scalar,
parser::DirectiveLocation::Object => DirectiveLocation::Object,
parser::DirectiveLocation::FieldDefinition => DirectiveLocation::FieldDefinition,
parser::DirectiveLocation::ArgumentDefinition => DirectiveLocation::ArgumentDefinition,
parser::DirectiveLocation::Interface => DirectiveLocation::Interface,
parser::DirectiveLocation::Union => DirectiveLocation::Union,
parser::DirectiveLocation::Enum => DirectiveLocation::Enum,
parser::DirectiveLocation::EnumValue => DirectiveLocation::EnumValue,
parser::DirectiveLocation::InputObject => DirectiveLocation::InputObject,
parser::DirectiveLocation::InputFieldDefinition => {
DirectiveLocation::InputFieldDefinition
}
parser::DirectiveLocation::VariableDefinition => DirectiveLocation::VariableDefinition,
}
}
}