use std::borrow::ToOwned;
use arcstr::ArcStr;
use derive_more::with_trait::Debug;
use crate::{
FieldError, IntoFieldError,
ast::{FromInputValue, InputValue, Type},
parser::{ParseError, ScalarToken},
schema::model::SchemaType,
types::base::TypeKind,
value::{DefaultScalarValue, ParseScalarValue},
};
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
pub enum DeprecationStatus {
Current,
Deprecated(Option<ArcStr>),
}
impl DeprecationStatus {
pub fn is_deprecated(&self) -> bool {
match self {
Self::Current => false,
Self::Deprecated(_) => true,
}
}
pub fn reason(&self) -> Option<&ArcStr> {
match self {
Self::Current => None,
Self::Deprecated(rsn) => rsn.as_ref(),
}
}
}
#[derive(Debug)]
pub struct ScalarMeta<S> {
#[doc(hidden)]
pub name: ArcStr,
#[doc(hidden)]
pub description: Option<ArcStr>,
#[doc(hidden)]
pub specified_by_url: Option<ArcStr>,
#[debug(ignore)]
pub(crate) try_parse_fn: InputValueParseFn<S>,
#[debug(ignore)]
pub(crate) parse_fn: ScalarTokenParseFn<S>,
}
impl<S> ScalarMeta<S> {
pub fn new<T>(name: impl Into<ArcStr>) -> Self
where
T: FromInputValue<S> + ParseScalarValue<S>,
T::Error: IntoFieldError<S>,
{
Self {
name: name.into(),
description: None,
specified_by_url: None,
try_parse_fn: try_parse_fn::<S, T>,
parse_fn: <T as ParseScalarValue<S>>::from_str,
}
}
#[must_use]
pub fn description(mut self, description: impl Into<ArcStr>) -> Self {
self.description = Some(description.into());
self
}
#[must_use]
pub fn specified_by_url(mut self, url: impl Into<ArcStr>) -> Self {
self.specified_by_url = Some(url.into());
self
}
pub fn into_meta(self) -> MetaType<S> {
MetaType::Scalar(self)
}
}
pub type InputValueParseFn<S> = for<'b> fn(&'b InputValue<S>) -> Result<(), FieldError<S>>;
pub type ScalarTokenParseFn<S> = for<'b> fn(ScalarToken<'b>) -> Result<S, ParseError>;
#[derive(Debug)]
pub struct ListMeta {
#[doc(hidden)]
pub of_type: Type,
#[doc(hidden)]
pub expected_size: Option<usize>,
}
impl ListMeta {
pub fn new(of_type: Type, expected_size: Option<usize>) -> Self {
Self {
of_type,
expected_size,
}
}
pub fn into_meta<S>(self) -> MetaType<S> {
MetaType::List(self)
}
}
#[derive(Debug)]
pub struct NullableMeta {
#[doc(hidden)]
pub of_type: Type,
}
impl NullableMeta {
pub fn new(of_type: Type) -> Self {
Self { of_type }
}
pub fn into_meta<S>(self) -> MetaType<S> {
MetaType::Nullable(self)
}
}
#[derive(Debug)]
pub struct ObjectMeta<S> {
#[doc(hidden)]
pub name: ArcStr,
#[doc(hidden)]
pub description: Option<ArcStr>,
#[doc(hidden)]
pub fields: Vec<Field<S>>,
#[doc(hidden)]
pub interface_names: Vec<ArcStr>,
}
impl<S> ObjectMeta<S> {
pub fn new(name: impl Into<ArcStr>, fields: &[Field<S>]) -> Self
where
S: Clone,
{
Self {
name: name.into(),
description: None,
fields: fields.to_vec(),
interface_names: vec![],
}
}
#[must_use]
pub fn description(mut self, description: impl Into<ArcStr>) -> Self {
self.description = Some(description.into());
self
}
#[must_use]
pub fn interfaces(mut self, interfaces: &[Type]) -> Self {
self.interface_names = interfaces
.iter()
.map(|t| t.innermost_name().into())
.collect();
self
}
pub fn into_meta(self) -> MetaType<S> {
MetaType::Object(self)
}
}
#[derive(Debug)]
pub struct EnumMeta<S> {
#[doc(hidden)]
pub name: ArcStr,
#[doc(hidden)]
pub description: Option<ArcStr>,
#[doc(hidden)]
pub values: Vec<EnumValue>,
#[debug(ignore)]
pub(crate) try_parse_fn: InputValueParseFn<S>,
}
impl<S> EnumMeta<S> {
pub fn new<T>(name: impl Into<ArcStr>, values: &[EnumValue]) -> Self
where
T: FromInputValue<S>,
T::Error: IntoFieldError<S>,
{
Self {
name: name.into(),
description: None,
values: values.to_owned(),
try_parse_fn: try_parse_fn::<S, T>,
}
}
#[must_use]
pub fn description(mut self, description: impl Into<ArcStr>) -> Self {
self.description = Some(description.into());
self
}
pub fn into_meta(self) -> MetaType<S> {
MetaType::Enum(self)
}
}
#[derive(Debug)]
pub struct InterfaceMeta<S> {
#[doc(hidden)]
pub name: ArcStr,
#[doc(hidden)]
pub description: Option<ArcStr>,
#[doc(hidden)]
pub fields: Vec<Field<S>>,
#[doc(hidden)]
pub interface_names: Vec<ArcStr>,
}
impl<S> InterfaceMeta<S> {
pub fn new(name: impl Into<ArcStr>, fields: &[Field<S>]) -> Self
where
S: Clone,
{
Self {
name: name.into(),
description: None,
fields: fields.to_vec(),
interface_names: Vec::new(),
}
}
#[must_use]
pub fn description(mut self, description: impl Into<ArcStr>) -> Self {
self.description = Some(description.into());
self
}
#[must_use]
pub fn interfaces(mut self, interfaces: &[Type]) -> Self {
self.interface_names = interfaces
.iter()
.map(|t| t.innermost_name().into())
.collect();
self
}
pub fn into_meta(self) -> MetaType<S> {
MetaType::Interface(self)
}
}
#[derive(Debug)]
pub struct UnionMeta {
#[doc(hidden)]
pub name: ArcStr,
#[doc(hidden)]
pub description: Option<ArcStr>,
#[doc(hidden)]
pub of_type_names: Vec<ArcStr>,
}
impl UnionMeta {
pub fn new(name: impl Into<ArcStr>, of_types: &[Type]) -> Self {
Self {
name: name.into(),
description: None,
of_type_names: of_types.iter().map(|t| t.innermost_name().into()).collect(),
}
}
#[must_use]
pub fn description(mut self, description: impl Into<ArcStr>) -> Self {
self.description = Some(description.into());
self
}
pub fn into_meta<S>(self) -> MetaType<S> {
MetaType::Union(self)
}
}
#[derive(Debug)]
pub struct InputObjectMeta<S> {
#[doc(hidden)]
pub name: ArcStr,
#[doc(hidden)]
pub description: Option<ArcStr>,
#[doc(hidden)]
pub input_fields: Vec<Argument<S>>,
#[debug(ignore)]
pub(crate) try_parse_fn: InputValueParseFn<S>,
}
impl<S> InputObjectMeta<S> {
pub fn new<T>(name: impl Into<ArcStr>, input_fields: &[Argument<S>]) -> Self
where
T: FromInputValue<S>,
T::Error: IntoFieldError<S>,
S: Clone,
{
Self {
name: name.into(),
description: None,
input_fields: input_fields.to_vec(),
try_parse_fn: try_parse_fn::<S, T>,
}
}
#[must_use]
pub fn description(mut self, description: impl Into<ArcStr>) -> Self {
self.description = Some(description.into());
self
}
pub fn into_meta(self) -> MetaType<S> {
MetaType::InputObject(self)
}
}
#[derive(Debug)]
pub struct PlaceholderMeta {
#[doc(hidden)]
pub of_type: Type,
}
#[derive(Debug, Clone)]
pub struct Field<S> {
#[doc(hidden)]
pub name: ArcStr,
#[doc(hidden)]
pub description: Option<ArcStr>,
#[doc(hidden)]
pub arguments: Option<Vec<Argument<S>>>,
#[doc(hidden)]
pub field_type: Type,
#[doc(hidden)]
pub deprecation_status: DeprecationStatus,
}
impl<S> Field<S> {
#[must_use]
pub fn description(mut self, description: impl Into<ArcStr>) -> Self {
self.description = Some(description.into());
self
}
#[must_use]
pub fn argument(mut self, argument: Argument<S>) -> Self {
match self.arguments {
None => {
self.arguments = Some(vec![argument]);
}
Some(ref mut args) => {
args.push(argument);
}
};
self
}
#[must_use]
pub fn is_builtin(&self) -> bool {
self.name.starts_with("__")
}
#[must_use]
pub fn deprecated(mut self, reason: Option<impl Into<ArcStr>>) -> Self {
self.deprecation_status = DeprecationStatus::Deprecated(reason.map(Into::into));
self
}
}
#[derive(Debug, Clone)]
pub struct Argument<S> {
#[doc(hidden)]
pub name: ArcStr,
#[doc(hidden)]
pub description: Option<ArcStr>,
#[doc(hidden)]
pub arg_type: Type,
#[doc(hidden)]
pub default_value: Option<InputValue<S>>,
}
impl<S> Argument<S> {
pub fn new(name: impl Into<ArcStr>, arg_type: Type) -> Self {
Self {
name: name.into(),
description: None,
arg_type,
default_value: None,
}
}
#[must_use]
pub fn description(mut self, description: impl Into<ArcStr>) -> Self {
self.description = Some(description.into());
self
}
#[must_use]
pub fn is_builtin(&self) -> bool {
self.name.starts_with("__")
}
#[must_use]
pub fn default_value(mut self, val: InputValue<S>) -> Self {
self.default_value = Some(val);
self
}
}
#[derive(Debug, Clone)]
pub struct EnumValue {
pub name: ArcStr,
pub description: Option<ArcStr>,
pub deprecation_status: DeprecationStatus,
}
impl EnumValue {
pub fn new(name: impl Into<ArcStr>) -> Self {
Self {
name: name.into(),
description: None,
deprecation_status: DeprecationStatus::Current,
}
}
#[must_use]
pub fn description(mut self, description: impl Into<ArcStr>) -> Self {
self.description = Some(description.into());
self
}
#[must_use]
pub fn deprecated(mut self, reason: Option<impl Into<ArcStr>>) -> Self {
self.deprecation_status = DeprecationStatus::Deprecated(reason.map(Into::into));
self
}
}
#[derive(Debug)]
pub enum MetaType<S = DefaultScalarValue> {
#[doc(hidden)]
Scalar(ScalarMeta<S>),
#[doc(hidden)]
List(ListMeta),
#[doc(hidden)]
Nullable(NullableMeta),
#[doc(hidden)]
Object(ObjectMeta<S>),
#[doc(hidden)]
Enum(EnumMeta<S>),
#[doc(hidden)]
Interface(InterfaceMeta<S>),
#[doc(hidden)]
Union(UnionMeta),
#[doc(hidden)]
InputObject(InputObjectMeta<S>),
#[doc(hidden)]
Placeholder(PlaceholderMeta),
}
impl<S> MetaType<S> {
pub fn name(&self) -> Option<&ArcStr> {
match self {
Self::Enum(EnumMeta { name, .. })
| Self::InputObject(InputObjectMeta { name, .. })
| Self::Interface(InterfaceMeta { name, .. })
| Self::Object(ObjectMeta { name, .. })
| Self::Scalar(ScalarMeta { name, .. })
| Self::Union(UnionMeta { name, .. }) => Some(name),
Self::List(..) | Self::Nullable(..) | Self::Placeholder(..) => None,
}
}
pub fn description(&self) -> Option<&ArcStr> {
match self {
Self::Enum(EnumMeta { description, .. })
| Self::InputObject(InputObjectMeta { description, .. })
| Self::Interface(InterfaceMeta { description, .. })
| Self::Object(ObjectMeta { description, .. })
| Self::Scalar(ScalarMeta { description, .. })
| Self::Union(UnionMeta { description, .. }) => description.as_ref(),
Self::List(..) | Self::Nullable(..) | Self::Placeholder(..) => None,
}
}
pub fn specified_by_url(&self) -> Option<&ArcStr> {
match self {
Self::Scalar(ScalarMeta {
specified_by_url, ..
}) => specified_by_url.as_ref(),
Self::Enum(..)
| Self::InputObject(..)
| Self::Interface(..)
| Self::List(..)
| Self::Nullable(..)
| Self::Object(..)
| Self::Placeholder(..)
| Self::Union(..) => None,
}
}
pub fn type_kind(&self) -> TypeKind {
match self {
Self::Scalar(..) => TypeKind::Scalar,
Self::List(..) => TypeKind::List,
Self::Nullable(..) => panic!("сan't take `type_kind` of `MetaType::Nullable`"),
Self::Object(..) => TypeKind::Object,
Self::Enum(..) => TypeKind::Enum,
Self::Interface(..) => TypeKind::Interface,
Self::Union(..) => TypeKind::Union,
Self::InputObject(..) => TypeKind::InputObject,
Self::Placeholder(..) => panic!("сan't take `type_kind` of `MetaType::Placeholder`"),
}
}
pub fn field_by_name(&self, name: &str) -> Option<&Field<S>> {
match self {
Self::Interface(InterfaceMeta { fields, .. })
| Self::Object(ObjectMeta { fields, .. }) => fields.iter().find(|f| f.name == name),
Self::Enum(..)
| Self::InputObject(..)
| Self::List(..)
| Self::Nullable(..)
| Self::Placeholder(..)
| Self::Scalar(..)
| Self::Union(..) => None,
}
}
pub fn input_field_by_name(&self, name: &str) -> Option<&Argument<S>> {
match self {
Self::InputObject(InputObjectMeta { input_fields, .. }) => {
input_fields.iter().find(|f| f.name == name)
}
Self::Enum(..)
| Self::Interface(..)
| Self::List(..)
| Self::Nullable(..)
| Self::Object(..)
| Self::Placeholder(..)
| Self::Scalar(..)
| Self::Union(..) => None,
}
}
pub fn as_type(&self) -> Type {
match self {
Self::Enum(EnumMeta { name, .. })
| Self::InputObject(InputObjectMeta { name, .. })
| Self::Interface(InterfaceMeta { name, .. })
| Self::Object(ObjectMeta { name, .. })
| Self::Scalar(ScalarMeta { name, .. })
| Self::Union(UnionMeta { name, .. }) => Type::nullable(name.clone()).wrap_non_null(),
Self::List(ListMeta {
of_type,
expected_size,
}) => of_type.clone().wrap_list(*expected_size).wrap_non_null(),
Self::Nullable(NullableMeta { of_type }) => of_type.clone().into_nullable(),
Self::Placeholder(PlaceholderMeta { of_type }) => of_type.clone(),
}
}
pub fn input_value_parse_fn(&self) -> Option<InputValueParseFn<S>> {
match self {
Self::Enum(EnumMeta { try_parse_fn, .. })
| Self::InputObject(InputObjectMeta { try_parse_fn, .. })
| Self::Scalar(ScalarMeta { try_parse_fn, .. }) => Some(*try_parse_fn),
Self::Interface(..)
| Self::List(..)
| Self::Nullable(..)
| Self::Object(..)
| Self::Placeholder(..)
| Self::Union(..) => None,
}
}
pub fn is_composite(&self) -> bool {
matches!(
self,
Self::Interface(..) | Self::Object(..) | Self::Union(..)
)
}
pub fn is_leaf(&self) -> bool {
matches!(self, Self::Enum(..) | Self::Scalar(..))
}
pub fn is_abstract(&self) -> bool {
matches!(self, Self::Interface(..) | Self::Union(..))
}
pub fn is_input(&self) -> bool {
matches!(
self,
Self::Enum(..) | Self::InputObject(..) | Self::Scalar(..)
)
}
pub fn is_builtin(&self) -> bool {
if let Some(name) = self.name() {
{
name.starts_with("__") ||
name == "Boolean" || name == "String" || name == "Int" || name == "Float" || name == "ID" ||
name == "_EmptyMutation" || name == "_EmptySubscription"
}
} else {
false
}
}
pub(crate) fn fields<'s>(&self, schema: &'s SchemaType<S>) -> Option<Vec<&'s Field<S>>> {
schema
.lookup_type(&self.as_type())
.and_then(|tpe| match tpe {
Self::Interface(i) => Some(i.fields.iter().collect()),
Self::Object(o) => Some(o.fields.iter().collect()),
Self::Union(u) => Some(
u.of_type_names
.iter()
.filter_map(|n| schema.concrete_type_by_name(n))
.filter_map(|t| t.fields(schema))
.flatten()
.collect(),
),
Self::Enum(..)
| Self::InputObject(..)
| Self::List(..)
| Self::Nullable(..)
| Self::Placeholder(..)
| Self::Scalar(..) => None,
})
}
}
fn try_parse_fn<S, T>(v: &InputValue<S>) -> Result<(), FieldError<S>>
where
T: FromInputValue<S>,
T::Error: IntoFieldError<S>,
{
T::from_input_value(v)
.map_err(T::Error::into_field_error)
.map(drop)
}