implemented directive (@include, @skip)
This commit is contained in:
parent
b7a65b6784
commit
92a95dbbf4
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "async-graphql"
|
||||
version = "0.5.0"
|
||||
version = "0.6.0"
|
||||
authors = ["sunli <scott_s829@163.com>"]
|
||||
edition = "2018"
|
||||
description = "The GraphQL server library implemented by rust"
|
||||
|
@ -17,7 +17,7 @@ readme = "README.md"
|
|||
default = ["chrono", "uuid"]
|
||||
|
||||
[dependencies]
|
||||
async-graphql-derive = { path = "async-graphql-derive", version = "0.5.0" }
|
||||
async-graphql-derive = { path = "async-graphql-derive", version = "0.6.0" }
|
||||
graphql-parser = "0.2.3"
|
||||
anyhow = "1.0.26"
|
||||
thiserror = "1.0.11"
|
||||
|
|
11
README.md
11
README.md
|
@ -74,9 +74,14 @@
|
|||
- [X] Parse value
|
||||
- [ ] Check type
|
||||
- [ ] Directives
|
||||
- [ ] @include
|
||||
- [ ] @skip
|
||||
- [ ] @deprecated
|
||||
- [X] @include
|
||||
- [X] FIELD
|
||||
- [ ] FRAGMENT_SPREAD
|
||||
- [ ] INLINE_FRAGMENT
|
||||
- [X] @skip
|
||||
- [X] FIELD
|
||||
- [ ] FRAGMENT_SPREAD
|
||||
- [ ] INLINE_FRAGMENT
|
||||
- [ ] Custom Directive
|
||||
- [X] Schema
|
||||
- [ ] Validation rules
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "async-graphql-derive"
|
||||
version = "0.5.0"
|
||||
version = "0.6.0"
|
||||
authors = ["sunli <scott_s829@163.com>"]
|
||||
edition = "2018"
|
||||
description = "The GraphQL server library implemented by rust"
|
||||
|
|
|
@ -87,7 +87,7 @@ pub fn generate(enum_args: &args::Enum, input: &DeriveInput) -> Result<TokenStre
|
|||
}
|
||||
|
||||
fn create_type_info(registry: &mut #crate_name::registry::Registry) -> String {
|
||||
registry.create_type(&Self::type_name(), |registry| {
|
||||
registry.create_type::<Self, _>(|registry| {
|
||||
#crate_name::registry::Type::Enum {
|
||||
name: #gql_typename,
|
||||
description: #desc,
|
||||
|
|
|
@ -102,7 +102,7 @@ pub fn generate(object_args: &args::InputObject, input: &DeriveInput) -> Result<
|
|||
}
|
||||
|
||||
fn create_type_info(registry: &mut #crate_name::registry::Registry) -> String {
|
||||
registry.create_type(&Self::type_name(), |registry| #crate_name::registry::Type::InputObject {
|
||||
registry.create_type::<Self, _>(|registry| #crate_name::registry::Type::InputObject {
|
||||
name: #gql_typename,
|
||||
description: #desc,
|
||||
input_fields: vec![#(#schema_fields),*]
|
||||
|
|
|
@ -149,10 +149,10 @@ pub fn generate(object_args: &args::Object, input: &DeriveInput) -> Result<Token
|
|||
}
|
||||
|
||||
fn create_type_info(registry: &mut #crate_name::registry::Registry) -> String {
|
||||
registry.create_type(&Self::type_name(), |registry| #crate_name::registry::Type::Object {
|
||||
registry.create_type::<Self, _>(|registry| #crate_name::registry::Type::Object {
|
||||
name: #gql_typename,
|
||||
description: #desc,
|
||||
fields: vec![#(#schema_fields),*]
|
||||
fields: vec![#(#schema_fields),*]
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -173,6 +173,9 @@ pub fn generate(object_args: &args::Object, input: &DeriveInput) -> Result<Token
|
|||
match selection {
|
||||
#crate_name::graphql_parser::query::Selection::Field(field) => {
|
||||
let ctx_field = ctx.with_item(field);
|
||||
if ctx_field.is_skip_this()? {
|
||||
continue;
|
||||
}
|
||||
if field.name.as_str() == "__typename" {
|
||||
let name = field.alias.clone().unwrap_or_else(|| field.name.clone());
|
||||
result.insert(name, #gql_typename.into());
|
||||
|
|
|
@ -43,12 +43,33 @@ impl MyObjFields for MyObj {
|
|||
#[async_std::main]
|
||||
async fn main() {
|
||||
let schema = async_graphql::Schema::<MyObj, async_graphql::GQLEmptyMutation>::new();
|
||||
let vars = async_graphql::Variables::parse_from_json(b"{\"myvar\": 999}").unwrap();
|
||||
let vars = async_graphql::Variables::parse_from_json(b"{\"myvar1\": 999}").unwrap();
|
||||
let res = schema
|
||||
.query(
|
||||
MyObj { value: 100 },
|
||||
async_graphql::GQLEmptyMutation,
|
||||
"query($myvar:Int) { a b(v:$myvar) }",
|
||||
r#"
|
||||
{
|
||||
__schema {
|
||||
directives @skip(if: true) {
|
||||
name
|
||||
description
|
||||
locations
|
||||
args {
|
||||
name description type {
|
||||
name
|
||||
description
|
||||
kind
|
||||
ofType {
|
||||
name
|
||||
description
|
||||
}
|
||||
} defaultValue
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
"#,
|
||||
)
|
||||
.variables(&vars)
|
||||
.execute()
|
||||
|
|
|
@ -45,7 +45,7 @@ impl<T: Scalar> GQLType for T {
|
|||
}
|
||||
|
||||
fn create_type_info(registry: &mut registry::Registry) -> String {
|
||||
registry.create_type(&T::type_name(), |_| registry::Type::Scalar {
|
||||
registry.create_type::<T, _>(|_| registry::Type::Scalar {
|
||||
name: T::type_name().to_string(),
|
||||
description: T::description(),
|
||||
})
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use crate::registry::Registry;
|
||||
use crate::{ErrorWithPosition, GQLInputValue, QueryError, Result};
|
||||
use crate::{ErrorWithPosition, GQLInputValue, GQLType, QueryError, Result};
|
||||
use fnv::FnvHasher;
|
||||
use graphql_parser::query::{Field, SelectionSet, Value, VariableDefinition};
|
||||
use std::any::{Any, TypeId};
|
||||
|
@ -118,6 +118,8 @@ impl<'a> ContextBase<'a, &'a Field> {
|
|||
if let Some(def) = def {
|
||||
if let Some(var_value) = self.variables.map(|vars| vars.get(&def.name)).flatten() {
|
||||
return Ok(var_value.clone());
|
||||
} else if let Some(default) = &def.default_value {
|
||||
return Ok(default.clone());
|
||||
}
|
||||
}
|
||||
return Err(QueryError::VarNotDefined {
|
||||
|
@ -177,4 +179,73 @@ impl<'a> ContextBase<'a, &'a Field> {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub fn is_skip_this(&self) -> Result<bool> {
|
||||
for directive in &self.directives {
|
||||
if directive.name == "skip" {
|
||||
if let Some(value) = directive
|
||||
.arguments
|
||||
.iter()
|
||||
.find(|(name, _)| name == "if")
|
||||
.map(|(_, value)| value)
|
||||
{
|
||||
let value = self.resolve_input_value(value.clone())?;
|
||||
let res: bool = GQLInputValue::parse(&value).ok_or_else(|| {
|
||||
QueryError::ExpectedType {
|
||||
expect: bool::qualified_type_name(),
|
||||
actual: value,
|
||||
}
|
||||
.with_position(directive.position)
|
||||
})?;
|
||||
if res {
|
||||
return Ok(true);
|
||||
}
|
||||
} else {
|
||||
return Err(QueryError::RequiredDirectiveArgs {
|
||||
directive: "@skip",
|
||||
arg_name: "if",
|
||||
arg_type: "Boolean!",
|
||||
}
|
||||
.with_position(directive.position)
|
||||
.into());
|
||||
}
|
||||
} else if directive.name == "include" {
|
||||
if let Some(value) = directive
|
||||
.arguments
|
||||
.iter()
|
||||
.find(|(name, _)| name == "if")
|
||||
.map(|(_, value)| value)
|
||||
{
|
||||
let value = self.resolve_input_value(value.clone())?;
|
||||
let res: bool = GQLInputValue::parse(&value).ok_or_else(|| {
|
||||
QueryError::ExpectedType {
|
||||
expect: bool::qualified_type_name(),
|
||||
actual: value,
|
||||
}
|
||||
.with_position(directive.position)
|
||||
})?;
|
||||
if !res {
|
||||
return Ok(true);
|
||||
}
|
||||
} else {
|
||||
return Err(QueryError::RequiredDirectiveArgs {
|
||||
directive: "@include",
|
||||
arg_name: "if",
|
||||
arg_type: "Boolean!",
|
||||
}
|
||||
.with_position(directive.position)
|
||||
.into());
|
||||
}
|
||||
} else {
|
||||
return Err(QueryError::UnknownDirective {
|
||||
name: directive.name.clone(),
|
||||
}
|
||||
.with_position(directive.position)
|
||||
.into());
|
||||
}
|
||||
}
|
||||
|
||||
Ok(false)
|
||||
}
|
||||
}
|
||||
|
|
12
src/error.rs
12
src/error.rs
|
@ -47,6 +47,18 @@ pub enum QueryError {
|
|||
|
||||
#[error("Variable \"${var_name}\" is not defined")]
|
||||
VarNotDefined { var_name: String },
|
||||
|
||||
#[error(
|
||||
"Directive \"{directive}\" argument \"{arg_name}\" of type \"{arg_type}\" is required, but it was not provided."
|
||||
)]
|
||||
RequiredDirectiveArgs {
|
||||
directive: &'static str,
|
||||
arg_name: &'static str,
|
||||
arg_type: &'static str,
|
||||
},
|
||||
|
||||
#[error("Unknown directive \"{name}\".")]
|
||||
UnknownDirective { name: String },
|
||||
}
|
||||
|
||||
pub trait ErrorWithPosition {
|
||||
|
|
|
@ -0,0 +1,109 @@
|
|||
use crate::model::__InputValue;
|
||||
use crate::{registry, Context, Result};
|
||||
use async_graphql_derive::{Enum, Object};
|
||||
|
||||
#[Enum(
|
||||
internal,
|
||||
desc = "A Directive can be adjacent to many parts of the GraphQL language, a __DirectiveLocation describes one such possible adjacencies."
|
||||
)]
|
||||
#[allow(non_camel_case_types)]
|
||||
pub enum __DirectiveLocation {
|
||||
#[item(desc = "Location adjacent to a query operation.")]
|
||||
QUERY,
|
||||
|
||||
#[item(desc = "Location adjacent to a mutation operation.")]
|
||||
MUTATION,
|
||||
|
||||
#[item(desc = "Location adjacent to a subscription operation.")]
|
||||
SUBSCRIPTION,
|
||||
|
||||
#[item(desc = "Location adjacent to a field.")]
|
||||
FIELD,
|
||||
|
||||
#[item(desc = "Location adjacent to a fragment definition.")]
|
||||
FRAGMENT_DEFINITION,
|
||||
|
||||
#[item(desc = "Location adjacent to a fragment spread.")]
|
||||
FRAGMENT_SPREAD,
|
||||
|
||||
#[item(desc = "Location adjacent to an inline fragment.")]
|
||||
INLINE_FRAGMENT,
|
||||
|
||||
#[item(desc = "Location adjacent to a variable definition.")]
|
||||
VARIABLE_DEFINITION,
|
||||
|
||||
#[item(desc = "Location adjacent to a schema definition.")]
|
||||
SCHEMA,
|
||||
|
||||
#[item(desc = "Location adjacent to a scalar definition.")]
|
||||
SCALAR,
|
||||
|
||||
#[item(desc = "Location adjacent to an object type definition.")]
|
||||
OBJECT,
|
||||
|
||||
#[item(desc = "Location adjacent to a field definition.")]
|
||||
FIELD_DEFINITION,
|
||||
|
||||
#[item(desc = "Location adjacent to an argument definition.")]
|
||||
ARGUMENT_DEFINITION,
|
||||
|
||||
#[item(desc = "Location adjacent to an interface definition.")]
|
||||
INTERFACE,
|
||||
|
||||
#[item(desc = "Location adjacent to a union definition.")]
|
||||
UNION,
|
||||
|
||||
#[item(desc = "Location adjacent to an enum definition.")]
|
||||
ENUM,
|
||||
|
||||
#[item(desc = "Location adjacent to an enum value definition.")]
|
||||
ENUM_VALUE,
|
||||
|
||||
#[item(desc = "Location adjacent to an input object type definition.")]
|
||||
INPUT_OBJECT,
|
||||
|
||||
#[item(desc = "Location adjacent to an input object field definition.")]
|
||||
INPUT_FIELD_DEFINITION,
|
||||
}
|
||||
|
||||
#[Object(
|
||||
internal,
|
||||
desc = r#"A Directive provides a way to describe alternate runtime execution and type validation behavior in a GraphQL document.
|
||||
|
||||
In some cases, you need to provide options to alter GraphQL's execution behavior in ways field arguments will not suffice, such as conditionally including or skipping a field. Directives provide this by describing additional information to the executor."#,
|
||||
field(name = "name", type = "String", owned),
|
||||
field(name = "description", type = "Option<String>", owned),
|
||||
field(name = "locations", type = "Vec<__DirectiveLocation>"),
|
||||
field(name = "args", type = "Vec<__InputValue>", owned)
|
||||
)]
|
||||
pub struct __Directive<'a> {
|
||||
pub registry: &'a registry::Registry,
|
||||
pub directive: &'a registry::Directive,
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl<'a> __DirectiveFields for __Directive<'a> {
|
||||
async fn name(&self, _: &Context<'_>) -> Result<String> {
|
||||
Ok(self.directive.name.to_string())
|
||||
}
|
||||
|
||||
async fn description(&self, _: &Context<'_>) -> Result<Option<String>> {
|
||||
Ok(self.directive.description.map(|s| s.to_string()))
|
||||
}
|
||||
|
||||
async fn locations<'b>(&'b self, _: &Context<'_>) -> Result<&'b Vec<__DirectiveLocation>> {
|
||||
Ok(&self.directive.locations)
|
||||
}
|
||||
|
||||
async fn args<'b>(&'b self, _: &Context<'_>) -> Result<Vec<__InputValue<'b>>> {
|
||||
Ok(self
|
||||
.directive
|
||||
.args
|
||||
.iter()
|
||||
.map(|input_value| __InputValue {
|
||||
registry: self.registry,
|
||||
input_value,
|
||||
})
|
||||
.collect())
|
||||
}
|
||||
}
|
|
@ -41,10 +41,7 @@ impl<'a> __FieldFields for __Field<'a> {
|
|||
}
|
||||
|
||||
async fn ty<'b>(&'b self, _: &Context<'_>) -> Result<__Type<'b>> {
|
||||
Ok(__Type {
|
||||
registry: self.registry,
|
||||
ty: &self.registry[&self.field.ty],
|
||||
})
|
||||
Ok(__Type::new(self.registry, &self.field.ty))
|
||||
}
|
||||
|
||||
async fn is_deprecated(&self, _: &Context<'_>) -> Result<bool> {
|
||||
|
|
|
@ -26,10 +26,7 @@ impl<'a> __InputValueFields for __InputValue<'a> {
|
|||
}
|
||||
|
||||
async fn ty<'b>(&'b self, _: &Context<'_>) -> Result<__Type<'b>> {
|
||||
Ok(__Type {
|
||||
registry: self.registry,
|
||||
ty: &self.registry[&self.input_value.ty],
|
||||
})
|
||||
Ok(__Type::new(self.registry, &self.input_value.ty))
|
||||
}
|
||||
|
||||
async fn default_value(&self, _: &Context<'_>) -> Result<Option<String>> {
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
mod directive;
|
||||
mod enum_value;
|
||||
mod field;
|
||||
mod input_value;
|
||||
|
@ -5,6 +6,7 @@ mod kind;
|
|||
mod schema;
|
||||
mod r#type;
|
||||
|
||||
pub use directive::{__Directive, __DirectiveLocation};
|
||||
pub use enum_value::__EnumValue;
|
||||
pub use field::__Field;
|
||||
pub use input_value::__InputValue;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use crate::model::__Type;
|
||||
use crate::model::{__Directive, __Type};
|
||||
use crate::registry;
|
||||
use crate::{Context, Result};
|
||||
use async_graphql_derive::Object;
|
||||
|
@ -23,6 +23,12 @@ use async_graphql_derive::Object;
|
|||
desc = "If this server supports mutation, the type that mutation operations will be rooted at.",
|
||||
type = "__Type",
|
||||
owned
|
||||
),
|
||||
field(
|
||||
name = "directives",
|
||||
desc = "A list of all directives supported by this server.",
|
||||
type = "Vec<__Directive>",
|
||||
owned
|
||||
)
|
||||
)]
|
||||
pub struct __Schema<'a> {
|
||||
|
@ -36,25 +42,35 @@ impl<'a> __SchemaFields for __Schema<'a> {
|
|||
async fn types<'b>(&'b self, _: &Context<'_>) -> Result<Vec<__Type<'b>>> {
|
||||
Ok(self
|
||||
.registry
|
||||
.types
|
||||
.values()
|
||||
.map(|ty| __Type {
|
||||
registry: &self.registry,
|
||||
ty,
|
||||
})
|
||||
.map(|ty| __Type::new_simple(self.registry, ty))
|
||||
.collect())
|
||||
}
|
||||
|
||||
async fn query_type<'b>(&'b self, _: &Context<'_>) -> Result<__Type<'b>> {
|
||||
Ok(__Type {
|
||||
registry: &self.registry,
|
||||
ty: &self.registry[self.query_type],
|
||||
})
|
||||
Ok(__Type::new_simple(
|
||||
self.registry,
|
||||
&self.registry.types[self.query_type],
|
||||
))
|
||||
}
|
||||
|
||||
async fn mutation_type<'b>(&'b self, _: &Context<'_>) -> Result<__Type<'b>> {
|
||||
Ok(__Type {
|
||||
registry: &self.registry,
|
||||
ty: &self.registry[self.mutation_type],
|
||||
})
|
||||
Ok(__Type::new_simple(
|
||||
self.registry,
|
||||
&self.registry.types[self.mutation_type],
|
||||
))
|
||||
}
|
||||
|
||||
async fn directives<'b>(&'b self, _: &Context<'_>) -> Result<Vec<__Directive<'b>>> {
|
||||
Ok(self
|
||||
.registry
|
||||
.directives
|
||||
.iter()
|
||||
.map(|directive| __Directive {
|
||||
registry: &self.registry,
|
||||
directive,
|
||||
})
|
||||
.collect())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,6 +3,12 @@ use crate::registry::Type;
|
|||
use crate::{registry, Context, Result};
|
||||
use async_graphql_derive::Object;
|
||||
|
||||
enum TypeDetail<'a> {
|
||||
Simple(&'a registry::Type),
|
||||
NonNull(&'a registry::Type),
|
||||
List(&'a registry::Type),
|
||||
}
|
||||
|
||||
#[Object(
|
||||
internal,
|
||||
desc = r#"
|
||||
|
@ -17,7 +23,7 @@ Depending on the kind of a type, certain fields describe information about that
|
|||
name = "fields",
|
||||
type = "Option<Vec<__Field>>",
|
||||
owned,
|
||||
arg(name = "includeDeprecated", type = "bool", default="false")
|
||||
arg(name = "includeDeprecated", type = "bool", default = "false")
|
||||
),
|
||||
field(name = "interfaces", type = "Option<Vec<__Type>>", owned),
|
||||
field(name = "possibleTypes", type = "Option<Vec<__Type>>", owned),
|
||||
|
@ -25,55 +31,91 @@ Depending on the kind of a type, certain fields describe information about that
|
|||
name = "enumValues",
|
||||
type = "Option<Vec<__EnumValue>>",
|
||||
owned,
|
||||
arg(name = "includeDeprecated", type = "bool", default="false")
|
||||
arg(name = "includeDeprecated", type = "bool", default = "false")
|
||||
),
|
||||
field(name = "inputFields", type = "Option<Vec<__InputValue>>", owned),
|
||||
field(name = "ofType", type = "Option<__Type>", owned)
|
||||
)]
|
||||
pub struct __Type<'a> {
|
||||
pub registry: &'a registry::Registry,
|
||||
pub ty: &'a registry::Type,
|
||||
registry: &'a registry::Registry,
|
||||
detail: TypeDetail<'a>,
|
||||
}
|
||||
|
||||
impl<'a> __Type<'a> {
|
||||
pub fn new_simple(registry: &'a registry::Registry, ty: &'a registry::Type) -> __Type<'a> {
|
||||
__Type {
|
||||
registry,
|
||||
detail: TypeDetail::Simple(ty),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new(registry: &'a registry::Registry, type_name: &str) -> __Type<'a> {
|
||||
if let Some(type_name) = parse_non_null(type_name) {
|
||||
__Type {
|
||||
registry,
|
||||
detail: TypeDetail::NonNull(®istry.types[type_name]),
|
||||
}
|
||||
} else if let Some(type_name) = parse_list(type_name) {
|
||||
__Type {
|
||||
registry,
|
||||
detail: TypeDetail::List(®istry.types[type_name]),
|
||||
}
|
||||
} else {
|
||||
__Type {
|
||||
registry,
|
||||
detail: TypeDetail::Simple(®istry.types[type_name]),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl<'a> __TypeFields for __Type<'a> {
|
||||
async fn kind(&self, _: &Context<'_>) -> Result<__TypeKind> {
|
||||
Ok(match self.ty {
|
||||
registry::Type::Scalar { .. } => __TypeKind::SCALAR,
|
||||
registry::Type::Object { .. } => __TypeKind::OBJECT,
|
||||
registry::Type::Interface { .. } => __TypeKind::INTERFACE,
|
||||
registry::Type::Union { .. } => __TypeKind::UNION,
|
||||
registry::Type::Enum { .. } => __TypeKind::ENUM,
|
||||
registry::Type::InputObject { .. } => __TypeKind::INPUT_OBJECT,
|
||||
registry::Type::List { .. } => __TypeKind::LIST,
|
||||
registry::Type::NonNull { .. } => __TypeKind::NON_NULL,
|
||||
})
|
||||
match &self.detail {
|
||||
TypeDetail::Simple(ty) => Ok(match ty {
|
||||
registry::Type::Scalar { .. } => __TypeKind::SCALAR,
|
||||
registry::Type::Object { .. } => __TypeKind::OBJECT,
|
||||
registry::Type::Interface { .. } => __TypeKind::INTERFACE,
|
||||
registry::Type::Union { .. } => __TypeKind::UNION,
|
||||
registry::Type::Enum { .. } => __TypeKind::ENUM,
|
||||
registry::Type::InputObject { .. } => __TypeKind::INPUT_OBJECT,
|
||||
}),
|
||||
TypeDetail::NonNull(_) => Ok(__TypeKind::NON_NULL),
|
||||
TypeDetail::List(_) => Ok(__TypeKind::LIST),
|
||||
}
|
||||
}
|
||||
|
||||
async fn name(&self, _: &Context<'_>) -> Result<Option<String>> {
|
||||
Ok(match self.ty {
|
||||
registry::Type::Scalar { name, .. } => Some(name.clone()),
|
||||
registry::Type::Object { name, .. } => Some(name.to_string()),
|
||||
registry::Type::Interface { name, .. } => Some(name.to_string()),
|
||||
registry::Type::Union { name, .. } => Some(name.to_string()),
|
||||
registry::Type::Enum { name, .. } => Some(name.to_string()),
|
||||
registry::Type::InputObject { name, .. } => Some(name.to_string()),
|
||||
registry::Type::List { .. } => None,
|
||||
registry::Type::NonNull { .. } => None,
|
||||
})
|
||||
match &self.detail {
|
||||
TypeDetail::Simple(ty) => Ok(match ty {
|
||||
registry::Type::Scalar { name, .. } => Some(name.clone()),
|
||||
registry::Type::Object { name, .. } => Some(name.to_string()),
|
||||
registry::Type::Interface { name, .. } => Some(name.to_string()),
|
||||
registry::Type::Union { name, .. } => Some(name.to_string()),
|
||||
registry::Type::Enum { name, .. } => Some(name.to_string()),
|
||||
registry::Type::InputObject { name, .. } => Some(name.to_string()),
|
||||
}),
|
||||
TypeDetail::NonNull(_) => Ok(None),
|
||||
TypeDetail::List(_) => Ok(None),
|
||||
}
|
||||
}
|
||||
|
||||
async fn description(&self, _: &Context<'_>) -> Result<Option<String>> {
|
||||
Ok(match self.ty {
|
||||
registry::Type::Scalar { description, .. } => description.map(|s| s.to_string()),
|
||||
registry::Type::Object { description, .. } => description.map(|s| s.to_string()),
|
||||
registry::Type::Interface { description, .. } => description.map(|s| s.to_string()),
|
||||
registry::Type::Union { description, .. } => description.map(|s| s.to_string()),
|
||||
registry::Type::Enum { description, .. } => description.map(|s| s.to_string()),
|
||||
registry::Type::InputObject { description, .. } => description.map(|s| s.to_string()),
|
||||
registry::Type::List { .. } => None,
|
||||
registry::Type::NonNull { .. } => None,
|
||||
})
|
||||
match &self.detail {
|
||||
TypeDetail::Simple(ty) => Ok(match ty {
|
||||
registry::Type::Scalar { description, .. } => description.map(|s| s.to_string()),
|
||||
registry::Type::Object { description, .. } => description.map(|s| s.to_string()),
|
||||
registry::Type::Interface { description, .. } => description.map(|s| s.to_string()),
|
||||
registry::Type::Union { description, .. } => description.map(|s| s.to_string()),
|
||||
registry::Type::Enum { description, .. } => description.map(|s| s.to_string()),
|
||||
registry::Type::InputObject { description, .. } => {
|
||||
description.map(|s| s.to_string())
|
||||
}
|
||||
}),
|
||||
TypeDetail::NonNull(_) => Ok(None),
|
||||
TypeDetail::List(_) => Ok(None),
|
||||
}
|
||||
}
|
||||
|
||||
async fn fields<'b>(
|
||||
|
@ -81,7 +123,7 @@ impl<'a> __TypeFields for __Type<'a> {
|
|||
_: &Context<'_>,
|
||||
include_deprecated: bool,
|
||||
) -> Result<Option<Vec<__Field<'b>>>> {
|
||||
if let Type::Object { fields, .. } = self.ty {
|
||||
if let TypeDetail::Simple(Type::Object { fields, .. }) = &self.detail {
|
||||
Ok(Some(
|
||||
fields
|
||||
.iter()
|
||||
|
@ -116,7 +158,7 @@ impl<'a> __TypeFields for __Type<'a> {
|
|||
_: &Context<'_>,
|
||||
include_deprecated: bool,
|
||||
) -> Result<Option<Vec<__EnumValue<'b>>>> {
|
||||
if let Type::Enum { enum_values, .. } = self.ty {
|
||||
if let TypeDetail::Simple(Type::Enum { enum_values, .. }) = &self.detail {
|
||||
Ok(Some(
|
||||
enum_values
|
||||
.iter()
|
||||
|
@ -139,7 +181,7 @@ impl<'a> __TypeFields for __Type<'a> {
|
|||
}
|
||||
|
||||
async fn input_fields<'b>(&'b self, _: &Context<'_>) -> Result<Option<Vec<__InputValue<'b>>>> {
|
||||
if let Type::InputObject { input_fields, .. } = self.ty {
|
||||
if let TypeDetail::Simple(Type::InputObject { input_fields, .. }) = &self.detail {
|
||||
Ok(Some(
|
||||
input_fields
|
||||
.iter()
|
||||
|
@ -155,6 +197,34 @@ impl<'a> __TypeFields for __Type<'a> {
|
|||
}
|
||||
|
||||
async fn of_type<'b>(&'b self, _: &Context<'_>) -> Result<Option<__Type<'b>>> {
|
||||
Ok(None)
|
||||
if let TypeDetail::List(ty) = &self.detail {
|
||||
Ok(Some(__Type {
|
||||
registry: self.registry,
|
||||
detail: TypeDetail::Simple(ty),
|
||||
}))
|
||||
} else if let TypeDetail::NonNull(ty) = &self.detail {
|
||||
Ok(Some(__Type {
|
||||
registry: self.registry,
|
||||
detail: TypeDetail::Simple(ty),
|
||||
}))
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_non_null(type_name: &str) -> Option<&str> {
|
||||
if type_name.ends_with("!") {
|
||||
Some(&type_name[..type_name.len() - 1])
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_list(type_name: &str) -> Option<&str> {
|
||||
if type_name.starts_with("[") {
|
||||
Some(&type_name[1..type_name.len() - 1])
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
use crate::{model, GQLType};
|
||||
use std::collections::HashMap;
|
||||
|
||||
pub struct InputValue {
|
||||
pub name: &'static str,
|
||||
pub description: Option<&'static str>,
|
||||
|
@ -50,10 +53,32 @@ pub enum Type {
|
|||
description: Option<&'static str>,
|
||||
input_fields: Vec<InputValue>,
|
||||
},
|
||||
List {
|
||||
of_type: usize,
|
||||
},
|
||||
NonNull {
|
||||
of_type: usize,
|
||||
},
|
||||
}
|
||||
|
||||
pub struct Directive {
|
||||
pub name: &'static str,
|
||||
pub description: Option<&'static str>,
|
||||
pub locations: Vec<model::__DirectiveLocation>,
|
||||
pub args: Vec<InputValue>,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct Registry {
|
||||
pub types: HashMap<String, Type>,
|
||||
pub directives: Vec<Directive>,
|
||||
}
|
||||
|
||||
impl Registry {
|
||||
pub fn create_type<T: GQLType, F: FnMut(&mut Registry) -> Type>(&mut self, mut f: F) -> String {
|
||||
let name = T::type_name();
|
||||
if !self.types.contains_key(name.as_ref()) {
|
||||
let ty = f(self);
|
||||
self.types.insert(name.to_string(), ty);
|
||||
}
|
||||
T::qualified_type_name()
|
||||
}
|
||||
|
||||
pub fn add_directive(&mut self, directive: Directive) {
|
||||
self.directives.push(directive);
|
||||
}
|
||||
}
|
|
@ -1,5 +0,0 @@
|
|||
mod registry;
|
||||
mod r#type;
|
||||
|
||||
pub use r#type::{EnumValue, Field, InputValue, Type};
|
||||
pub use registry::Registry;
|
|
@ -1,26 +0,0 @@
|
|||
use crate::registry::Type;
|
||||
use std::collections::HashMap;
|
||||
use std::ops::Deref;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct Registry {
|
||||
types: HashMap<String, Type>,
|
||||
}
|
||||
|
||||
impl Deref for Registry {
|
||||
type Target = HashMap<String, Type>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.types
|
||||
}
|
||||
}
|
||||
|
||||
impl Registry {
|
||||
pub fn create_type<F: FnMut(&mut Registry) -> Type>(&mut self, name: &str, mut f: F) -> String {
|
||||
if !self.types.contains_key(name) {
|
||||
let ty = f(self);
|
||||
self.types.insert(name.to_string(), ty);
|
||||
}
|
||||
name.to_string()
|
||||
}
|
||||
}
|
|
@ -31,7 +31,7 @@ impl<'a> GQLType for &'a str {
|
|||
}
|
||||
|
||||
fn create_type_info(registry: &mut registry::Registry) -> String {
|
||||
registry.create_type(&Self::type_name(), |_| registry::Type::Scalar {
|
||||
registry.create_type::<Self, _>(|_| registry::Type::Scalar {
|
||||
name: Self::type_name().to_string(),
|
||||
description: Some(STRING_DESC),
|
||||
})
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
use crate::registry::Registry;
|
||||
use crate::model::__DirectiveLocation;
|
||||
use crate::registry::{Directive, InputValue, Registry};
|
||||
use crate::types::QueryRoot;
|
||||
use crate::{
|
||||
ContextBase, Data, ErrorWithPosition, GQLObject, GQLOutputValue, QueryError, QueryParseError,
|
||||
Result, Variables,
|
||||
ContextBase, Data, ErrorWithPosition, GQLObject, GQLOutputValue, GQLType, QueryError,
|
||||
QueryParseError, Result, Variables,
|
||||
};
|
||||
use graphql_parser::parse_query;
|
||||
use graphql_parser::query::{Definition, OperationDefinition};
|
||||
|
@ -16,9 +17,41 @@ pub struct Schema<Query, Mutation> {
|
|||
|
||||
impl<Query: GQLObject, Mutation: GQLObject> Schema<Query, Mutation> {
|
||||
pub fn new() -> Self {
|
||||
let mut registry = Default::default();
|
||||
let mut registry = Registry::default();
|
||||
|
||||
registry.add_directive(Directive {
|
||||
name: "include",
|
||||
description: Some("Directs the executor to include this field or fragment only when the `if` argument is true."),
|
||||
locations: vec![__DirectiveLocation::FIELD],
|
||||
args: vec![InputValue{
|
||||
name: "if",
|
||||
description: Some("Included when true."),
|
||||
ty: "Boolean!".to_string(),
|
||||
default_value: None
|
||||
}]
|
||||
});
|
||||
|
||||
registry.add_directive(Directive {
|
||||
name: "skip",
|
||||
description: Some("Directs the executor to skip this field or fragment when the `if` argument is true."),
|
||||
locations: vec![__DirectiveLocation::FIELD],
|
||||
args: vec![InputValue{
|
||||
name: "if",
|
||||
description: Some("Skipped when true."),
|
||||
ty: "Boolean!".to_string(),
|
||||
default_value: None
|
||||
}]
|
||||
});
|
||||
|
||||
// register scalars
|
||||
bool::create_type_info(&mut registry);
|
||||
i32::create_type_info(&mut registry);
|
||||
f32::create_type_info(&mut registry);
|
||||
String::create_type_info(&mut registry);
|
||||
|
||||
Query::create_type_info(&mut registry);
|
||||
Mutation::create_type_info(&mut registry);
|
||||
|
||||
Self {
|
||||
mark_query: PhantomData,
|
||||
mark_mutation: PhantomData,
|
||||
|
|
|
@ -12,7 +12,7 @@ impl GQLType for GQLEmptyMutation {
|
|||
}
|
||||
|
||||
fn create_type_info(registry: &mut registry::Registry) -> String {
|
||||
registry.create_type(&Self::type_name(), |_| registry::Type::Object {
|
||||
registry.create_type::<Self, _>(|_| registry::Type::Object {
|
||||
name: "EmptyMutation",
|
||||
description: None,
|
||||
fields: Vec::new(),
|
||||
|
|
|
@ -3,7 +3,11 @@ use std::borrow::Cow;
|
|||
|
||||
impl<T: GQLType> GQLType for Vec<T> {
|
||||
fn type_name() -> Cow<'static, str> {
|
||||
Cow::Owned(format!("[{}]", T::qualified_type_name()))
|
||||
T::type_name()
|
||||
}
|
||||
|
||||
fn qualified_type_name() -> String {
|
||||
format!("[{}]", T::qualified_type_name())
|
||||
}
|
||||
|
||||
fn create_type_info(registry: &mut registry::Registry) -> String {
|
||||
|
|
Loading…
Reference in New Issue