implemented directive (@include, @skip)

This commit is contained in:
sunli 2020-03-04 14:24:44 +08:00
parent b7a65b6784
commit 92a95dbbf4
23 changed files with 451 additions and 117 deletions

View File

@ -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"

View File

@ -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

View File

@ -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"

View File

@ -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,

View File

@ -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),*]

View File

@ -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());

View File

@ -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()

View File

@ -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(),
})

View File

@ -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)
}
}

View File

@ -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 {

109
src/model/directive.rs Normal file
View File

@ -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())
}
}

View File

@ -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> {

View File

@ -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>> {

View File

@ -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;

View File

@ -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())
}
}

View File

@ -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(&registry.types[type_name]),
}
} else if let Some(type_name) = parse_list(type_name) {
__Type {
registry,
detail: TypeDetail::List(&registry.types[type_name]),
}
} else {
__Type {
registry,
detail: TypeDetail::Simple(&registry.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
}
}

View File

@ -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);
}
}

View File

@ -1,5 +0,0 @@
mod registry;
mod r#type;
pub use r#type::{EnumValue, Field, InputValue, Type};
pub use registry::Registry;

View File

@ -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()
}
}

View File

@ -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),
})

View File

@ -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,

View File

@ -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(),

View File

@ -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 {