async-graphql/src/model/type.rs

249 lines
8.1 KiB
Rust

use std::collections::HashSet;
use crate::{
model::{__EnumValue, __Field, __InputValue, __TypeKind},
registry,
registry::is_visible,
Context, Object,
};
enum TypeDetail<'a> {
Named(&'a registry::MetaType),
NonNull(String),
List(String),
}
pub struct __Type<'a> {
registry: &'a registry::Registry,
visible_types: &'a HashSet<&'a str>,
detail: TypeDetail<'a>,
}
impl<'a> __Type<'a> {
#[inline]
pub fn new_simple(
registry: &'a registry::Registry,
visible_types: &'a HashSet<&'a str>,
ty: &'a registry::MetaType,
) -> __Type<'a> {
__Type {
registry,
visible_types,
detail: TypeDetail::Named(ty),
}
}
#[inline]
pub fn new(
registry: &'a registry::Registry,
visible_types: &'a HashSet<&'a str>,
type_name: &str,
) -> __Type<'a> {
match registry::MetaTypeName::create(type_name) {
registry::MetaTypeName::NonNull(ty) => __Type {
registry,
visible_types,
detail: TypeDetail::NonNull(ty.to_string()),
},
registry::MetaTypeName::List(ty) => __Type {
registry,
visible_types,
detail: TypeDetail::List(ty.to_string()),
},
registry::MetaTypeName::Named(ty) => __Type {
registry,
visible_types,
detail: TypeDetail::Named(match registry.types.get(ty) {
Some(t) => t,
None => panic!("Type '{}' not found!", ty),
}),
},
}
}
}
/// The fundamental unit of any GraphQL Schema is the type. There are many kinds
/// of types in GraphQL as represented by the `__TypeKind` enum.
///
/// Depending on the kind of a type, certain fields describe information about
/// that type. Scalar types provide no information beyond a name and
/// description, while Enum types provide their values. Object and Interface
/// types provide the fields they describe. Abstract types, Union and Interface,
/// provide the Object types possible at runtime. List and NonNull types compose
/// other types.
#[Object(internal, name = "__Type")]
impl<'a> __Type<'a> {
#[inline]
async fn kind(&self) -> __TypeKind {
match &self.detail {
TypeDetail::Named(ty) => match ty {
registry::MetaType::Scalar { .. } => __TypeKind::Scalar,
registry::MetaType::Object { .. } => __TypeKind::Object,
registry::MetaType::Interface { .. } => __TypeKind::Interface,
registry::MetaType::Union { .. } => __TypeKind::Union,
registry::MetaType::Enum { .. } => __TypeKind::Enum,
registry::MetaType::InputObject { .. } => __TypeKind::InputObject,
},
TypeDetail::NonNull(_) => __TypeKind::NonNull,
TypeDetail::List(_) => __TypeKind::List,
}
}
#[inline]
async fn name(&self) -> Option<&str> {
match &self.detail {
TypeDetail::Named(ty) => Some(ty.name()),
TypeDetail::NonNull(_) => None,
TypeDetail::List(_) => None,
}
}
#[inline]
async fn description(&self) -> Option<&str> {
match &self.detail {
TypeDetail::Named(ty) => match ty {
registry::MetaType::Scalar { description, .. }
| registry::MetaType::Object { description, .. }
| registry::MetaType::Interface { description, .. }
| registry::MetaType::Union { description, .. }
| registry::MetaType::Enum { description, .. }
| registry::MetaType::InputObject { description, .. } => description.as_deref(),
},
TypeDetail::NonNull(_) => None,
TypeDetail::List(_) => None,
}
}
async fn fields(
&self,
ctx: &Context<'_>,
#[graphql(default = false)] include_deprecated: bool,
) -> Option<Vec<__Field<'a>>> {
if let TypeDetail::Named(ty) = &self.detail {
ty.fields().map(|fields| {
fields
.values()
.filter(|field| is_visible(ctx, &field.visible))
.filter(|field| {
(include_deprecated || !field.deprecation.is_deprecated())
&& !field.name.starts_with("__")
})
.map(|field| __Field {
registry: self.registry,
visible_types: self.visible_types,
field,
})
.collect()
})
} else {
None
}
}
async fn interfaces(&self) -> Option<Vec<__Type<'a>>> {
if let TypeDetail::Named(registry::MetaType::Object { name, .. }) = &self.detail {
Some(
self.registry
.implements
.get(name)
.unwrap_or(&Default::default())
.iter()
.filter(|ty| self.visible_types.contains(ty.as_str()))
.map(|ty| __Type::new(self.registry, self.visible_types, ty))
.collect(),
)
} else {
None
}
}
async fn possible_types(&self) -> Option<Vec<__Type<'a>>> {
if let TypeDetail::Named(registry::MetaType::Interface { possible_types, .. })
| TypeDetail::Named(registry::MetaType::Union { possible_types, .. }) = &self.detail
{
Some(
possible_types
.iter()
.filter(|ty| self.visible_types.contains(ty.as_str()))
.map(|ty| __Type::new(self.registry, self.visible_types, ty))
.collect(),
)
} else {
None
}
}
async fn enum_values(
&self,
ctx: &Context<'_>,
#[graphql(default = false)] include_deprecated: bool,
) -> Option<Vec<__EnumValue<'a>>> {
if let TypeDetail::Named(registry::MetaType::Enum { enum_values, .. }) = &self.detail {
Some(
enum_values
.values()
.filter(|value| is_visible(ctx, &value.visible))
.filter(|value| include_deprecated || !value.deprecation.is_deprecated())
.map(|value| __EnumValue {
registry: self.registry,
value,
})
.collect(),
)
} else {
None
}
}
async fn input_fields(&self, ctx: &Context<'_>) -> Option<Vec<__InputValue<'a>>> {
if let TypeDetail::Named(registry::MetaType::InputObject { input_fields, .. }) =
&self.detail
{
Some(
input_fields
.values()
.filter(|input_value| is_visible(ctx, &input_value.visible))
.map(|input_value| __InputValue {
registry: self.registry,
visible_types: self.visible_types,
input_value,
})
.collect(),
)
} else {
None
}
}
#[inline]
async fn of_type(&self) -> Option<__Type<'a>> {
if let TypeDetail::List(ty) = &self.detail {
Some(__Type::new(self.registry, self.visible_types, &ty))
} else if let TypeDetail::NonNull(ty) = &self.detail {
Some(__Type::new(self.registry, self.visible_types, &ty))
} else {
None
}
}
#[graphql(name = "specifiedByURL")]
async fn specified_by_url(&self) -> Option<&'a str> {
if let TypeDetail::Named(registry::MetaType::Scalar {
specified_by_url, ..
}) = &self.detail
{
*specified_by_url
} else {
None
}
}
async fn one_of(&self) -> Option<bool> {
if let TypeDetail::Named(registry::MetaType::InputObject { oneof, .. }) = &self.detail {
Some(*oneof)
} else {
None
}
}
}