add schema model

This commit is contained in:
sunli 2020-03-02 19:25:21 +08:00
parent 332faa70fa
commit f090df76f7
13 changed files with 339 additions and 337 deletions

View File

@ -19,3 +19,4 @@ proc-macro = true
proc-macro2 = "1.0.6"
syn = { version = "1.0.13", features = ["full"] }
quote = "1.0.2"
Inflector = "0.11.4"

View File

@ -1,28 +1,25 @@
use syn::{Attribute, AttributeArgs, Error, Meta, NestedMeta, Result, Type};
use syn::{Attribute, AttributeArgs, Error, Meta, MetaList, NestedMeta, Result, Type};
#[derive(Debug)]
pub struct Object {
pub internal: bool,
pub auto_impl: bool,
pub name: Option<String>,
pub desc: Option<String>,
pub fields: Vec<Field>,
}
impl Object {
pub fn parse(args: AttributeArgs) -> Result<Self> {
let mut internal = false;
let mut auto_impl = false;
let mut name = None;
let mut desc = None;
let mut fields = Vec::new();
for arg in args {
match arg {
NestedMeta::Meta(Meta::Path(p)) if p.is_ident("internal") => {
internal = true;
}
NestedMeta::Meta(Meta::Path(p)) if p.is_ident("auto_impl") => {
auto_impl = true;
}
NestedMeta::Meta(Meta::NameValue(nv)) => {
if nv.path.is_ident("name") {
if let syn::Lit::Str(lit) = nv.lit {
@ -44,15 +41,18 @@ impl Object {
}
}
}
NestedMeta::Meta(Meta::List(ls)) if ls.path.is_ident("field") => {
fields.push(Field::parse(&ls)?);
}
_ => {}
}
}
Ok(Self {
internal,
auto_impl,
name,
desc,
fields,
})
}
}
@ -66,34 +66,82 @@ pub struct Argument {
#[derive(Debug)]
pub struct Field {
pub name: Option<String>,
pub name: String,
pub resolver: Option<String>,
pub desc: Option<String>,
pub is_attr: bool,
pub attr_type: Option<Type>,
pub ty: Type,
pub is_owned: bool,
pub arguments: Vec<Argument>,
}
impl Field {
pub fn parse(attrs: &[Attribute]) -> Result<Option<Self>> {
let mut is_field = false;
fn parse(ls: &MetaList) -> Result<Self> {
let mut name = None;
let mut resolver = None;
let mut desc = None;
let mut is_attr = false;
let mut attr_type = None;
let mut ty = None;
let mut is_owned = false;
let mut arguments = Vec::new();
for attr in attrs {
if attr.path.is_ident("field") {
is_field = true;
if let Ok(Meta::List(args)) = attr.parse_meta() {
for meta in args.nested {
match meta {
NestedMeta::Meta(Meta::Path(p)) if p.is_ident("attr") => {
is_attr = true;
for meta in &ls.nested {
match meta {
NestedMeta::Meta(Meta::Path(p)) if p.is_ident("owned") => {
is_owned = true;
}
NestedMeta::Meta(Meta::NameValue(nv)) => {
if nv.path.is_ident("name") {
if let syn::Lit::Str(lit) = &nv.lit {
name = Some(lit.value());
} else {
return Err(Error::new_spanned(
&nv.lit,
"Attribute 'name' should be a string.",
));
}
} else if nv.path.is_ident("desc") {
if let syn::Lit::Str(lit) = &nv.lit {
desc = Some(lit.value());
} else {
return Err(Error::new_spanned(
&nv.lit,
"Attribute 'desc' should be a string.",
));
}
} else if nv.path.is_ident("resolver") {
if let syn::Lit::Str(lit) = &nv.lit {
resolver = Some(lit.value());
} else {
return Err(Error::new_spanned(
&nv.lit,
"Attribute 'resolver' should be a string.",
));
}
} else if nv.path.is_ident("type") {
if let syn::Lit::Str(lit) = &nv.lit {
if let Ok(ty2) = syn::parse_str::<syn::Type>(&lit.value()) {
ty = Some(ty2);
} else {
return Err(Error::new_spanned(&lit, "Expect type"));
}
NestedMeta::Meta(Meta::NameValue(nv)) => {
desc = Some(lit.value());
} else {
return Err(Error::new_spanned(
&nv.lit,
"Attribute 'type' should be a string.",
));
}
}
}
NestedMeta::Meta(Meta::List(ls)) => {
if ls.path.is_ident("arg") {
let mut name = None;
let mut desc = None;
let mut ty = None;
for meta in &ls.nested {
if let NestedMeta::Meta(Meta::NameValue(nv)) = meta {
if nv.path.is_ident("name") {
if let syn::Lit::Str(lit) = nv.lit {
if let syn::Lit::Str(lit) = &nv.lit {
name = Some(lit.value());
} else {
return Err(Error::new_spanned(
@ -102,7 +150,7 @@ impl Field {
));
}
} else if nv.path.is_ident("desc") {
if let syn::Lit::Str(lit) = nv.lit {
if let syn::Lit::Str(lit) = &nv.lit {
desc = Some(lit.value());
} else {
return Err(Error::new_spanned(
@ -110,102 +158,58 @@ impl Field {
"Attribute 'desc' should be a string.",
));
}
} else if nv.path.is_ident("attr_type") {
} else if nv.path.is_ident("type") {
if let syn::Lit::Str(lit) = &nv.lit {
if let Ok(ty) = syn::parse_str::<syn::Type>(&lit.value()) {
attr_type = Some(ty);
if let Ok(ty2) = syn::parse_str::<syn::Type>(&lit.value()) {
ty = Some(ty2);
} else {
return Err(Error::new_spanned(&lit, "expect type"));
}
} else {
return Err(Error::new_spanned(
&nv.lit,
"Attribute 'attr_type' should be a string.",
"Attribute 'type' should be a string.",
));
}
}
}
NestedMeta::Meta(Meta::List(ls)) => {
if ls.path.is_ident("arg") {
let mut name = None;
let mut desc = None;
let mut ty = None;
for meta in &ls.nested {
if let NestedMeta::Meta(Meta::NameValue(nv)) = meta {
if nv.path.is_ident("name") {
if let syn::Lit::Str(lit) = &nv.lit {
name = Some(lit.value());
} else {
return Err(Error::new_spanned(
&nv.lit,
"Attribute 'name' should be a string.",
));
}
} else if nv.path.is_ident("desc") {
if let syn::Lit::Str(lit) = &nv.lit {
desc = Some(lit.value());
} else {
return Err(Error::new_spanned(
&nv.lit,
"Attribute 'desc' should be a string.",
));
}
} else if nv.path.is_ident("type") {
if let syn::Lit::Str(lit) = &nv.lit {
if let Ok(ty2) =
syn::parse_str::<syn::Type>(&lit.value())
{
ty = Some(ty2);
} else {
return Err(Error::new_spanned(
&lit,
"expect type",
));
}
} else {
return Err(Error::new_spanned(
&nv.lit,
"Attribute 'type' should be a string.",
));
}
}
}
}
if name.is_none() {
return Err(Error::new_spanned(ls, "missing name."));
}
if ty.is_none() {
return Err(Error::new_spanned(ls, "missing type."));
}
arguments.push(Argument {
name: name.unwrap(),
desc,
ty: ty.unwrap(),
});
}
}
_ => {}
}
if name.is_none() {
return Err(Error::new_spanned(ls, "missing name."));
}
if ty.is_none() {
return Err(Error::new_spanned(ls, "missing type."));
}
arguments.push(Argument {
name: name.unwrap(),
desc,
ty: ty.unwrap(),
});
}
}
_ => {}
}
}
if is_field {
Ok(Some(Self {
name,
desc,
is_attr,
attr_type,
arguments,
}))
} else {
Ok(None)
if name.is_none() {
return Err(Error::new_spanned(ls, "missing name."));
}
if ty.is_none() {
return Err(Error::new_spanned(ls, "missing type."));
}
Ok(Self {
name: name.unwrap(),
resolver,
desc,
ty: ty.unwrap(),
is_owned,
arguments,
})
}
}

View File

@ -81,7 +81,7 @@ pub fn generate(enum_args: &args::Enum, input: &DeriveInput) -> Result<TokenStre
#[#crate_name::async_trait::async_trait]
impl #crate_name::GQLOutputValue for #ident {
async fn resolve(&self, _: &#crate_name::ContextSelectionSet<'_>) -> #crate_name::Result<serde_json::Value> {
self.resolve_enum()
#crate_name::GQLEnum::resolve_enum(self)
}
}
};

View File

@ -46,6 +46,8 @@ pub fn generate(object_args: &args::Object, input: &DeriveInput) -> Result<Token
impl #crate_name::GQLInputValue for #ident {
fn parse(value: #crate_name::Value) -> #crate_name::Result<Self> {
use #crate_name::GQLType;
if let #crate_name::Value::Object(mut obj) = value {
#(#get_fields)*
Ok(Self { #(#fields),* })
@ -58,6 +60,7 @@ pub fn generate(object_args: &args::Object, input: &DeriveInput) -> Result<Token
}
fn parse_from_json(value: #crate_name::serde_json::Value) -> #crate_name::Result<Self> {
use #crate_name::GQLType;
if let #crate_name::serde_json::Value::Object(mut obj) = value {
#(#get_json_fields)*
Ok(Self { #(#fields),* })

View File

@ -1,5 +1,6 @@
use crate::args;
use crate::utils::get_crate_name;
use inflector::Inflector;
use proc_macro::TokenStream;
use proc_macro2::Span;
use quote::quote;
@ -7,11 +8,10 @@ use syn::{Data, DeriveInput, Error, Ident, Result};
pub fn generate(object_args: &args::Object, input: &DeriveInput) -> Result<TokenStream> {
let crate_name = get_crate_name(object_args.internal);
let attrs = &input.attrs;
let vis = &input.vis;
let ident = &input.ident;
let s = match &input.data {
Data::Struct(s) => s,
match &input.data {
Data::Struct(_) => {}
_ => return Err(Error::new_spanned(input, "It should be a struct.")),
};
@ -20,94 +20,63 @@ pub fn generate(object_args: &args::Object, input: &DeriveInput) -> Result<Token
.clone()
.unwrap_or_else(|| ident.to_string());
let trait_ident = Ident::new(&format!("{}Fields", ident.to_string()), Span::call_site());
let mut obj_attrs = Vec::new();
let mut obj_fields = Vec::new();
let mut trait_fns = Vec::new();
let mut resolvers = Vec::new();
let mut all_is_simple_attr = true;
for field in &s.fields {
if let Some(field_args) = args::Field::parse(&field.attrs)? {
// is field
let vis = &field.vis;
let ty = &field.ty;
let ident = field.ident.as_ref().unwrap();
let field_name = ident.to_string();
for field in &object_args.fields {
let ty = &field.ty;
let field_name = &field.name;
let mut decl_params = Vec::new();
let mut get_params = Vec::new();
let mut use_params = Vec::new();
obj_fields.push(field);
if field_args.is_attr {
let ty = field_args.attr_type.as_ref().unwrap_or(ty);
obj_attrs.push(quote! { #vis #ident: #ty });
if !field_args.arguments.is_empty() || field_args.attr_type.is_some() {
all_is_simple_attr = false;
}
} else {
all_is_simple_attr = false;
}
for arg in &field.arguments {
let name = Ident::new(&arg.name, Span::call_site());
let ty = &arg.ty;
let name_str = name.to_string();
let snake_case_name = Ident::new(&name.to_string().to_snake_case(), ident.span());
let mut decl_params = Vec::new();
let mut get_params = Vec::new();
let mut use_params = Vec::new();
for arg in &field_args.arguments {
let name = Ident::new(&arg.name, Span::call_site());
let ty = &arg.ty;
let name_str = name.to_string();
decl_params.push(quote! { #name: #ty });
get_params.push(quote! {
let #name: #ty = ctx_field.param_value(#name_str)?;
});
use_params.push(quote! { #name });
}
decl_params.push(quote! { #snake_case_name: #ty });
get_params.push(quote! {
let #snake_case_name: #ty = ctx_field.param_value(#name_str)?;
});
use_params.push(quote! { #snake_case_name });
}
let resolver = Ident::new(
&field
.resolver
.as_ref()
.unwrap_or(&field.name.to_snake_case()),
Span::call_site(),
);
if field.is_owned {
trait_fns.push(quote! {
async fn #ident(&self, ctx: &#crate_name::ContextField<'_>, #(#decl_params),*) -> #crate_name::Result<#ty>;
});
resolvers.push(quote! {
if field.name.as_str() == #field_name {
#(#get_params)*
let obj = #trait_ident::#ident(self, &ctx_field, #(#use_params),*).await.
map_err(|err| err.with_position(field.position))?;
let ctx_obj = ctx_field.with_item(&field.selection_set);
let value = obj.resolve(&ctx_obj).await.
map_err(|err| err.with_position(field.position))?;
let name = field.alias.clone().unwrap_or_else(|| field.name.clone());
result.insert(name, value.into());
continue;
}
});
async fn #resolver(&self, ctx: &#crate_name::ContextField<'_>, #(#decl_params),*) -> #crate_name::Result<#ty>;
});
} else {
obj_attrs.push(quote! { #field });
trait_fns.push(quote! {
async fn #resolver<'a>(&'a self, ctx: &#crate_name::ContextField<'_>, #(#decl_params),*) -> #crate_name::Result<&'a #ty>;
});
}
}
let mut impl_fields = quote! {};
if object_args.auto_impl && all_is_simple_attr {
let mut impl_fns = Vec::new();
for field in obj_fields {
let ident = &field.ident;
let ty = &field.ty;
impl_fns.push(quote! {
async fn #ident(&self, _: &#crate_name::ContextField<'_>) -> #crate_name::Result<#ty> {
Ok(self.#ident.clone())
}
});
}
impl_fields = quote! {
#[#crate_name::async_trait::async_trait]
impl #trait_ident for #ident {
#(#impl_fns)*
resolvers.push(quote! {
if field.name.as_str() == #field_name {
#(#get_params)*
let obj = #trait_ident::#resolver(self, &ctx_field, #(#use_params),*).await.
map_err(|err| err.with_position(field.position))?;
let ctx_obj = ctx_field.with_item(&field.selection_set);
let value = obj.resolve(&ctx_obj).await.
map_err(|err| err.with_position(field.position))?;
let name = field.alias.clone().unwrap_or_else(|| field.name.clone());
result.insert(name, value.into());
continue;
}
};
});
}
let expanded = quote! {
#(#attrs)*
#vis struct #ident {
#(#obj_attrs),*
}
#input
#[#crate_name::async_trait::async_trait]
#vis trait #trait_ident {
@ -156,8 +125,6 @@ pub fn generate(object_args: &args::Object, input: &DeriveInput) -> Result<Token
}
impl #crate_name::GQLObject for #ident {}
#impl_fields
};
Ok(expanded.into())
}

View File

@ -1,112 +1,39 @@
use async_graphql::*;
#[async_graphql::Enum(name = "haha", desc = "hehe")]
enum MyEnum {
A,
B,
}
#[async_graphql::InputObject]
struct MyInputObj {
a: i32,
b: i32,
}
#[async_graphql::Object(auto_impl)]
struct MyObj2 {
#[field(attr)]
a: i32,
#[field(attr)]
b: i32,
}
#[async_graphql::Object(name = "haha", desc = "hehe")]
#[async_graphql::Object(
field(name = "a", type = "i32"),
field(owned, name = "b", type = "i32"),
field(owned, name = "c", type = "Option<String>")
)]
struct MyObj {
#[field(
attr,
name = "a",
arg(name = "a1", type = "i32"),
arg(name = "a2", type = "f32")
)]
a: i32,
#[field(desc = "hehe")]
b: i32,
#[field(arg(name = "input", type = "MyEnum"))]
c: MyEnum,
#[field(arg(name = "input", type = "MyInputObj"))]
d: i32,
#[field]
e: &'static str,
#[field]
f: &'static [i32],
#[field]
g: &'static MyObj2,
#[field]
child: ChildObj,
}
#[async_graphql::Object]
struct ChildObj {
#[field(attr)]
value: f32,
value: i32,
}
#[async_trait::async_trait]
impl MyObjFields for MyObj {
async fn a(&self, ctx: &ContextField<'_>, a1: i32, a2: f32) -> Result<i32> {
Ok(a1 + a2 as i32)
async fn a<'a>(
&'a self,
ctx: &async_graphql::ContextField<'_>,
) -> async_graphql::Result<&'a i32> {
Ok(&self.value)
}
async fn b(&self, ctx: &ContextField<'_>) -> Result<i32> {
async fn b(&self, ctx: &async_graphql::ContextField<'_>) -> async_graphql::Result<i32> {
Ok(999)
}
async fn c(&self, ctx: &ContextField<'_>, input: MyEnum) -> Result<MyEnum> {
Ok(input)
}
async fn d(&self, ctx: &ContextField<'_>, input: MyInputObj) -> Result<i32> {
Ok(input.a + input.b)
}
async fn e(&self, ctx: &ContextField<'_>) -> Result<&'static str> {
Ok("hehehe")
}
async fn f(&self, ctx: &ContextField<'_>) -> Result<&'static [i32]> {
Ok(&[1, 2, 3, 4, 5])
}
async fn g(&self, ctx: &ContextField<'_>) -> Result<&'static MyObj2> {
Ok(&MyObj2 { a: 10, b: 20 })
}
async fn child(&self, ctx: &ContextField<'_>) -> async_graphql::Result<ChildObj> {
Ok(ChildObj { value: 10.0 })
}
}
#[async_trait::async_trait]
impl ChildObjFields for ChildObj {
async fn value(&self, ctx: &ContextField<'_>) -> Result<f32> {
Ok(self.value)
async fn c(
&self,
ctx: &async_graphql::ContextField<'_>,
) -> async_graphql::Result<Option<String>> {
Ok(Some(format!("**{}**", self.value)))
}
}
#[async_std::main]
async fn main() {
let res = QueryBuilder::new(
MyObj { a: 10 },
GQLEmptyMutation,
"{ b c(input:B) d(input:{a:10 b:20}) e f g { a b } }",
let res = async_graphql::QueryBuilder::new(
MyObj { value: 100 },
async_graphql::GQLEmptyMutation,
"{ a b c }",
)
.execute()
.await

31
src/schema/enum_value.rs Normal file
View File

@ -0,0 +1,31 @@
use crate::{ContextField, Result};
use async_graphql_derive::Object;
#[Object(
internal,
desc = "One possible value for a given Enum. Enum values are unique values, not a placeholder for a string or numeric value. However an Enum value is returned in a JSON response as a string.",
field(name = "name", type = "String", owned),
field(name = "description", type = "Option<String>", owned),
field(name = "isDeprecated", type = "bool", owned),
field(name = "deprecationReason", type = "Option<String>", owned)
)]
pub struct __EnumValue {}
#[async_trait::async_trait]
impl __EnumValueFields for __EnumValue {
async fn name(&self, _: &ContextField<'_>) -> Result<String> {
todo!()
}
async fn description(&self, _: &ContextField<'_>) -> Result<Option<String>> {
todo!()
}
async fn is_deprecated(&self, _: &ContextField<'_>) -> Result<bool> {
todo!()
}
async fn deprecation_reason(&self, _: &ContextField<'_>) -> Result<Option<String>> {
todo!()
}
}

View File

@ -1,23 +1,38 @@
use crate::schema::{__InputValue, __Type};
use crate::schema::__Type;
use crate::{ContextField, Result};
use async_graphql_derive::Object;
#[Object(internal, auto_impl)]
pub struct __Field {
#[field(attr)]
name: &'static str,
#[Object(
internal,
desc = "Object and Interface types are described by a list of Fields, each of which has a name, potentially a list of arguments, and a return type.",
field(name = "name", type = "String", owned),
field(name = "description", type = "Option<String>", owned),
field(name = "type", resolver = "ty", type = "__Type", owned),
field(name = "isDeprecated", type = "bool", owned),
field(name = "deprecationReason", type = "Option<String>", owned)
)]
pub struct __Field {}
#[field(attr)]
description: Option<&'static str>,
#[async_trait::async_trait]
#[allow()]
impl __FieldFields for __Field {
async fn name(&self, _: &ContextField<'_>) -> Result<String> {
todo!()
}
#[field(attr)]
args: &'static [__InputValue],
async fn description(&self, _: &ContextField<'_>) -> Result<Option<String>> {
todo!()
}
#[field(attr, name = "type")]
ty: &'static __Type,
async fn ty(&self, _: &ContextField<'_>) -> Result<__Type> {
todo!()
}
#[field(attr, name = "isDeprecated")]
is_deprecated: bool,
async fn is_deprecated(&self, _: &ContextField<'_>) -> Result<bool> {
todo!()
}
#[field(attr, name = "deprecationReason")]
deprecation_reason: Option<String>,
async fn deprecation_reason(&self, _: &ContextField<'_>) -> Result<Option<String>> {
todo!()
}
}

View File

@ -1,18 +1,32 @@
use crate::schema::__Type;
use crate::Value;
use crate::{ContextField, Result};
use async_graphql_derive::Object;
#[Object(internal)]
pub struct __InputValue {
#[field(attr)]
name: &'static str,
#[Object(
internal,
desc = "Arguments provided to Fields or Directives and the input fields of an InputObject are represented as Input Values which describe their type and optionally a default value.",
field(name = "name", type = "String", owned),
field(name = "description", type = "Option<String>", owned),
field(name = "type", resolver = "ty", type = "__Type", owned),
field(name = "defaultValue", type = "String", owned)
)]
pub struct __InputValue {}
#[field(attr)]
description: Option<&'static str>,
#[async_trait::async_trait]
impl __InputValueFields for __InputValue {
async fn name(&self, _: &ContextField<'_>) -> Result<String> {
todo!()
}
#[field(attr, name = "type")]
ty: &'static __Type,
async fn description(&self, _: &ContextField<'_>) -> Result<Option<String>> {
todo!()
}
#[field(attr, attr_type = "Value", name = "defaultValue")]
default_value: String,
async fn ty(&self, _: &ContextField<'_>) -> Result<__Type> {
todo!()
}
async fn default_value(&self, _: &ContextField<'_>) -> Result<String> {
todo!()
}
}

View File

@ -1,6 +1,9 @@
use async_graphql_derive::Enum;
#[Enum(internal)]
#[Enum(
internal,
desc = "An enum describing what kind of type a given `__Type` is."
)]
#[allow(non_camel_case_types)]
pub enum __TypeKind {
#[item(desc = "Indicates this type is a scalar.")]

View File

@ -1,8 +1,10 @@
mod enum_value;
mod field;
mod input_value;
mod kind;
mod r#type;
pub use enum_value::__EnumValue;
pub use field::__Field;
pub use input_value::__InputValue;
pub use kind::__TypeKind;

View File

@ -1,36 +1,70 @@
use crate::schema::{__Field, __InputValue, __TypeKind};
use crate::schema::{__EnumValue, __InputValue, __TypeKind};
use crate::{ContextField, Result};
use async_graphql_derive::Object;
#[Object(internal)]
pub struct __Type {
#[field(attr)]
kind: __TypeKind,
#[Object(
internal,
desc = r#"
The fundamental unit of any GraphQL Schema is the type. There are many kinds of types in GraphQL as represented by the `__TypeKind` enum.
#[field(attr)]
name: Option<&'static str>,
#[field(attr)]
description: Option<&'static str>,
#[field(attr, arg(name = "includeDeprecated", type = "bool"))]
fields: Option<&'static [__Field]>,
#[field(attr)]
interfaces: Option<&'static [__Type]>,
#[field(attr, name = "possibleTypes")]
possible_types: Option<&'static [__Type]>,
#[field(
attr,
name = "enumValues",
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.
"#,
field(name = "kind", type = "__TypeKind", owned),
field(name = "name", type = "String", owned),
field(name = "description", type = "Option<String>", owned),
field(
name = "fields",
type = "Option<Vec<__Type>>",
owned,
arg(name = "includeDeprecated", type = "bool")
)]
enum_values: Option<&'static [__Type]>,
),
field(name = "interfaces", type = "Option<Vec<__Type>>", owned),
field(name = "possibleTypes", type = "Option<Vec<__Type>>", owned),
field(name = "enumValues", type = "Option<Vec<__EnumValue>>", owned),
field(name = "inputFields", type = "Option<Vec<__InputValue>>", owned),
field(name = "ofType", type = "Option<__Type>", owned)
)]
pub struct __Type {}
#[field(attr, name = "inputFields")]
input_fields: Option<&'static [__InputValue]>,
#[async_trait::async_trait]
impl __TypeFields for __Type {
async fn kind<'a>(&'a self, _: &ContextField<'_>) -> Result<__TypeKind> {
todo!()
}
#[field(attr, name = "ofType")]
of_type: Option<&'static __Type>,
async fn name<'a>(&'a self, _: &ContextField<'_>) -> Result<String> {
todo!()
}
async fn description<'a>(&'a self, _: &ContextField<'_>) -> Result<Option<String>> {
todo!()
}
async fn fields<'a>(
&'a self,
_: &ContextField<'_>,
include_deprecated: bool,
) -> Result<Option<Vec<__Type>>> {
todo!()
}
async fn interfaces<'a>(&'a self, _: &ContextField<'_>) -> Result<Option<Vec<__Type>>> {
todo!()
}
async fn possible_types<'a>(&'a self, _: &ContextField<'_>) -> Result<Option<Vec<__Type>>> {
todo!()
}
async fn enum_values<'a>(&'a self, _: &ContextField<'_>) -> Result<Option<Vec<__EnumValue>>> {
todo!()
}
async fn input_fields<'a>(&'a self, _: &ContextField<'_>) -> Result<Option<Vec<__InputValue>>> {
todo!()
}
async fn of_type<'a>(&'a self, _: &ContextField<'_>) -> Result<Option<__Type>> {
todo!()
}
}

View File

@ -3,7 +3,8 @@ use std::borrow::Cow;
impl<T: GQLType> GQLType for Option<T> {
fn type_name() -> Cow<'static, str> {
Cow::Owned(format!("{}", T::type_name().trim_end_matches("!")))
let name = T::type_name();
Cow::Owned(format!("{}", &name[..name.len() - 1]))
}
}
@ -24,12 +25,12 @@ impl<T: GQLInputValue> GQLInputValue for Option<T> {
}
#[async_trait::async_trait]
impl<T: GQLOutputValue + Send + Sync> GQLOutputValue for Option<T> {
async fn resolve(&self, ctx: &ContextSelectionSet<'_>) -> Result<serde_json::Value> {
impl<T: GQLOutputValue + Sync> GQLOutputValue for Option<T> {
async fn resolve(&self, ctx: &ContextSelectionSet<'_>) -> Result<serde_json::Value> where {
if let Some(inner) = self {
inner.resolve(ctx).await
} else {
Ok(serde_json::Value::Null)
}
}
}
}