use crate::parser::Pos; use crate::registry::Registry; use crate::{ registry, Context, ContextSelectionSet, FieldResult, InputValueResult, Positioned, QueryError, Result, Value, ID, }; use std::borrow::Cow; use std::future::Future; use std::pin::Pin; use std::sync::Arc; /// Represents a GraphQL type /// /// All GraphQL types implement this trait, such as `Scalar`, `Object`, `Union` ... pub trait Type { /// Type the name. fn type_name() -> Cow<'static, str>; /// Qualified typename. fn qualified_type_name() -> String { format!("{}!", Self::type_name()) } /// Introspection type name /// /// Is the return value of field `__typename`, the interface and union should return the current type, and the others return `Type::type_name`. fn introspection_type_name(&self) -> Cow<'static, str> { Self::type_name() } /// Create type information in the registry and return qualified typename. fn create_type_info(registry: &mut registry::Registry) -> String; /// Returns a `GlobalID` that is unique among all types. fn global_id(id: ID) -> ID { base64::encode(format!("{}:{}", Self::type_name(), *id)).into() } /// Parse `GlobalID`. fn from_global_id(id: ID) -> Option { let v: Vec<&str> = id.splitn(2, ':').collect(); if v.len() != 2 { return None; } if v[0] != Self::type_name() { return None; } Some(v[1].to_string().into()) } } /// Represents a GraphQL input value pub trait InputValueType: Type + Sized { /// Parse from `Value` fn parse(value: &Value) -> InputValueResult; } /// Represents a GraphQL output value #[async_trait::async_trait] pub trait OutputValueType: Type { /// Resolve an output value to `serde_json::Value`. async fn resolve(&self, ctx: &ContextSelectionSet<'_>, pos: Pos) -> Result; } #[allow(missing_docs)] pub type BoxFieldFuture<'a> = Pin> + 'a + Send>>; /// Represents a GraphQL object #[async_trait::async_trait] pub trait ObjectType: OutputValueType { /// This function returns true of type `EmptyMutation` only #[doc(hidden)] fn is_empty() -> bool { false } /// Resolves a field value and outputs it as a json value `serde_json::Value`. async fn resolve_field(&self, ctx: &Context<'_>) -> Result; /// Collect the fields with the `name` inline object fn collect_inline_fields<'a>( &'a self, name: &Positioned, ctx: &ContextSelectionSet<'a>, futures: &mut Vec>, ) -> Result<()> where Self: Send + Sync + Sized, { if name.as_str() == Self::type_name().as_ref() || ctx .registry .implements .get(Self::type_name().as_ref()) .map(|ty| ty.contains(name.as_str())) .unwrap_or_default() { crate::collect_fields(ctx, self, futures) } else { Ok(()) } } /// Query entities with params async fn find_entity(&self, ctx: &Context<'_>, _params: &Value) -> Result { Err(QueryError::EntityNotFound.into_error(ctx.position())) } } /// Represents a GraphQL input object pub trait InputObjectType: InputValueType {} /// Represents a GraphQL scalar /// /// You can implement the trait to create a custom scalar. /// /// # Examples /// /// ```rust /// use async_graphql::*; /// /// struct MyInt(i32); /// /// #[Scalar] /// impl ScalarType for MyInt { /// fn type_name() -> &'static str { /// "MyInt" /// } /// /// fn parse(value: &Value) -> InputValueResult { /// if let Value::Int(n) = value { /// Ok(MyInt(*n as i32)) /// } else { /// Err(InputValueError::ExpectedType) /// } /// } /// /// fn to_json(&self) -> Result { /// Ok(self.0.into()) /// } /// } /// ``` pub trait ScalarType: Sized + Send { /// The type name of a scalar. fn type_name() -> &'static str; /// The description of a scalar. fn description() -> Option<&'static str> { None } /// Parse a scalar value, return `Some(Self)` if successful, otherwise return `None`. fn parse(value: &Value) -> InputValueResult; /// Checks for a valid scalar value. /// /// The default implementation is to try to parse it, and in some cases you can implement this on your own to improve performance. fn is_valid(value: &Value) -> bool { match Self::parse(value) { Ok(_) => true, _ => false, } } /// Convert the scalar value to json value. fn to_json(&self) -> Result; } impl Type for &T { fn type_name() -> Cow<'static, str> { T::type_name() } fn create_type_info(registry: &mut Registry) -> String { T::create_type_info(registry) } } #[async_trait::async_trait] impl OutputValueType for &T { #[allow(clippy::trivially_copy_pass_by_ref)] async fn resolve(&self, ctx: &ContextSelectionSet<'_>, pos: Pos) -> Result { T::resolve(*self, ctx, pos).await } } impl Type for Box { fn type_name() -> Cow<'static, str> { T::type_name() } fn create_type_info(registry: &mut Registry) -> String { T::create_type_info(registry) } } #[async_trait::async_trait] impl OutputValueType for Box { #[allow(clippy::trivially_copy_pass_by_ref)] #[allow(clippy::borrowed_box)] async fn resolve(&self, ctx: &ContextSelectionSet<'_>, pos: Pos) -> Result { T::resolve(&*self, ctx, pos).await } } impl Type for Arc { fn type_name() -> Cow<'static, str> { T::type_name() } fn create_type_info(registry: &mut Registry) -> String { T::create_type_info(registry) } } #[async_trait::async_trait] impl OutputValueType for Arc { #[allow(clippy::trivially_copy_pass_by_ref)] async fn resolve(&self, ctx: &ContextSelectionSet<'_>, pos: Pos) -> Result { T::resolve(&*self, ctx, pos).await } } impl Type for FieldResult { fn type_name() -> Cow<'static, str> { T::type_name() } fn qualified_type_name() -> String { T::qualified_type_name() } fn create_type_info(registry: &mut registry::Registry) -> String { T::create_type_info(registry) } } #[async_trait::async_trait] impl OutputValueType for FieldResult { async fn resolve( &self, ctx: &ContextSelectionSet<'_>, pos: Pos, ) -> crate::Result { match self { Ok(value) => Ok(OutputValueType::resolve(value, ctx, pos).await?), Err(err) => Err(err.clone().into_error_with_path( pos, match &ctx.path_node { Some(path) => path.to_json(), None => serde_json::Value::Null, }, )), } } }