This commit is contained in:
sunli 2020-04-27 12:57:52 +08:00
parent 001319776f
commit 4659da9c30
27 changed files with 241 additions and 410 deletions

View File

@ -219,7 +219,6 @@ pub struct Field {
impl Field {
pub fn parse(attrs: &[Attribute]) -> Result<Option<Self>> {
let mut is_field = false;
let mut name = None;
let mut desc = None;
let mut deprecation = None;
@ -231,13 +230,12 @@ impl Field {
for attr in attrs {
match attr.parse_meta()? {
Meta::Path(p) if p.is_ident("field") => {
is_field = true;
}
Meta::List(ls) if ls.path.is_ident("field") => {
is_field = true;
for meta in &ls.nested {
match meta {
NestedMeta::Meta(Meta::Path(p)) if p.is_ident("skip") => {
return Ok(None);
}
NestedMeta::Meta(Meta::Path(p)) if p.is_ident("external") => {
external = true;
}
@ -305,20 +303,16 @@ impl Field {
}
}
if is_field {
Ok(Some(Self {
name,
desc,
deprecation,
cache_control,
external,
provides,
requires,
is_ref,
}))
} else {
Ok(None)
}
Ok(Some(Self {
name,
desc,
deprecation,
cache_control,
external,
provides,
requires,
is_ref,
}))
}
}
@ -452,49 +446,58 @@ impl InputField {
if attr.path.is_ident("field") {
if let Meta::List(args) = &attr.parse_meta()? {
for meta in &args.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("default") {
if let syn::Lit::Str(lit) = &nv.lit {
match parse_value(&lit.value()) {
Ok(Value::Variable(_)) => {
return Err(Error::new_spanned(
&lit,
"The default cannot be a variable",
))
}
Ok(value) => default = Some(value),
Err(err) => {
return Err(Error::new_spanned(
&lit,
format!("Invalid value: {}", err),
));
}
match meta {
NestedMeta::Meta(Meta::Path(p)) if p.is_ident("skip") => {
return Err(Error::new_spanned(
meta,
"Fields on InputObject are not allowed to be skipped",
));
}
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("default") {
if let syn::Lit::Str(lit) = &nv.lit {
match parse_value(&lit.value()) {
Ok(Value::Variable(_)) => {
return Err(Error::new_spanned(
&lit,
"The default cannot be a variable",
))
}
Ok(value) => default = Some(value),
Err(err) => {
return Err(Error::new_spanned(
&lit,
format!("Invalid value: {}", err),
));
}
}
} else {
return Err(Error::new_spanned(
&nv.lit,
"Attribute 'default' should be a string.",
));
}
} else {
return Err(Error::new_spanned(
&nv.lit,
"Attribute 'default' should be a string.",
));
}
}
_ => {}
}
}

View File

@ -42,7 +42,130 @@ pub fn generate(object_args: &args::Object, item_impl: &mut ItemImpl) -> Result<
for item in &mut item_impl.items {
if let ImplItem::Method(method) = item {
if let Some(field) = args::Field::parse(&method.attrs)? {
if method.attrs.iter().any(|attr| attr.path.is_ident("entity")) {
let ty = match &method.sig.output {
ReturnType::Type(_, ty) => OutputType::parse(ty)?,
ReturnType::Default => {
return Err(Error::new_spanned(&method.sig.output, "Missing type"))
}
};
let mut arg_ctx = false;
let mut args = Vec::new();
for (idx, arg) in method.sig.inputs.iter_mut().enumerate() {
if let FnArg::Receiver(receiver) = arg {
if idx != 0 {
return Err(Error::new_spanned(
receiver,
"The self receiver must be the first parameter.",
));
}
} else if let FnArg::Typed(pat) = arg {
if idx == 0 {
return Err(Error::new_spanned(
pat,
"The self receiver must be the first parameter.",
));
}
match (&*pat.pat, &*pat.ty) {
(Pat::Ident(arg_ident), Type::Path(arg_ty)) => {
args.push((
arg_ident,
arg_ty,
args::Argument::parse(&crate_name, &pat.attrs)?,
));
pat.attrs.clear();
}
(_, Type::Reference(TypeReference { elem, .. })) => {
if let Type::Path(path) = elem.as_ref() {
if idx != 1
|| path.path.segments.last().unwrap().ident != "Context"
{
return Err(Error::new_spanned(
arg,
"The Context must be the second argument.",
));
}
arg_ctx = true;
}
}
_ => return Err(Error::new_spanned(arg, "Invalid argument type.")),
}
}
}
let entity_type = ty.value_type();
let mut key_pat = Vec::new();
let mut key_getter = Vec::new();
let mut use_keys = Vec::new();
let mut keys = Vec::new();
let mut keys_str = String::new();
for (ident, ty, args::Argument { name, .. }) in &args {
let name = name
.clone()
.unwrap_or_else(|| ident.ident.to_string().to_camel_case());
if !keys_str.is_empty() {
keys_str.push(' ');
}
keys_str.push_str(&name);
key_pat.push(quote! {
Some(#ident)
});
key_getter.push(quote! {
params.get(#name).and_then(|value| {
let value: Option<#ty> = #crate_name::InputValueType::parse(value);
value
})
});
keys.push(name);
use_keys.push(ident);
}
add_keys.push(quote! { registry.add_keys(&<#entity_type as #crate_name::Type>::type_name(), #keys_str); });
create_entity_types.push(
quote! { <#entity_type as #crate_name::Type>::create_type_info(registry); },
);
let field_ident = &method.sig.ident;
let ctx_param = if arg_ctx {
quote! { &ctx, }
} else {
quote! {}
};
let do_find = match &ty {
OutputType::Value(_) => quote! {
self.#field_ident(#ctx_param #(#use_keys),*).await
},
OutputType::Result(_, _) => {
quote! { self.#field_ident(#ctx_param #(#use_keys),*).await? }
}
};
find_entities.push((
args.len(),
quote! {
if typename == &<#entity_type as #crate_name::Type>::type_name() {
if let (#(#key_pat),*) = (#(#key_getter),*) {
let ctx_obj = ctx.with_selection_set(&ctx.selection_set);
return #crate_name::OutputValueType::resolve(&#do_find, &ctx_obj, pos).await;
}
}
},
));
method.attrs.remove(
method
.attrs
.iter()
.enumerate()
.find(|(_, a)| a.path.is_ident("entity"))
.map(|(idx, _)| idx)
.unwrap(),
);
} else if let Some(field) = args::Field::parse(&method.attrs)? {
if method.sig.asyncness.is_none() {
return Err(Error::new_spanned(
&method.sig.output,
@ -240,138 +363,14 @@ pub fn generate(object_args: &args::Object, item_impl: &mut ItemImpl) -> Result<
}
});
method.attrs.remove(
method
.attrs
.iter()
.enumerate()
.find(|(_, a)| a.path.is_ident("field"))
.map(|(idx, _)| idx)
.unwrap(),
);
} else if method.attrs.iter().any(|attr| attr.path.is_ident("entity")) {
let ty = match &method.sig.output {
ReturnType::Type(_, ty) => OutputType::parse(ty)?,
ReturnType::Default => {
return Err(Error::new_spanned(&method.sig.output, "Missing type"))
}
};
let mut arg_ctx = false;
let mut args = Vec::new();
for (idx, arg) in method.sig.inputs.iter_mut().enumerate() {
if let FnArg::Receiver(receiver) = arg {
if idx != 0 {
return Err(Error::new_spanned(
receiver,
"The self receiver must be the first parameter.",
));
}
} else if let FnArg::Typed(pat) = arg {
if idx == 0 {
return Err(Error::new_spanned(
pat,
"The self receiver must be the first parameter.",
));
}
match (&*pat.pat, &*pat.ty) {
(Pat::Ident(arg_ident), Type::Path(arg_ty)) => {
args.push((
arg_ident,
arg_ty,
args::Argument::parse(&crate_name, &pat.attrs)?,
));
pat.attrs.clear();
}
(_, Type::Reference(TypeReference { elem, .. })) => {
if let Type::Path(path) = elem.as_ref() {
if idx != 1
|| path.path.segments.last().unwrap().ident != "Context"
{
return Err(Error::new_spanned(
arg,
"The Context must be the second argument.",
));
}
arg_ctx = true;
}
}
_ => return Err(Error::new_spanned(arg, "Invalid argument type.")),
}
}
if let Some((idx, _)) = method
.attrs
.iter()
.enumerate()
.find(|(_, a)| a.path.is_ident("field"))
{
method.attrs.remove(idx);
}
let entity_type = ty.value_type();
let mut key_pat = Vec::new();
let mut key_getter = Vec::new();
let mut use_keys = Vec::new();
let mut keys = Vec::new();
let mut keys_str = String::new();
for (ident, ty, args::Argument { name, .. }) in &args {
let name = name
.clone()
.unwrap_or_else(|| ident.ident.to_string().to_camel_case());
if !keys_str.is_empty() {
keys_str.push(' ');
}
keys_str.push_str(&name);
key_pat.push(quote! {
Some(#ident)
});
key_getter.push(quote! {
params.get(#name).and_then(|value| {
let value: Option<#ty> = #crate_name::InputValueType::parse(value);
value
})
});
keys.push(name);
use_keys.push(ident);
}
add_keys.push(quote! { registry.add_keys(&<#entity_type as #crate_name::Type>::type_name(), #keys_str); });
create_entity_types.push(
quote! { <#entity_type as #crate_name::Type>::create_type_info(registry); },
);
let field_ident = &method.sig.ident;
let ctx_param = if arg_ctx {
quote! { &ctx, }
} else {
quote! {}
};
let do_find = match &ty {
OutputType::Value(_) => quote! {
self.#field_ident(#ctx_param #(#use_keys),*).await
},
OutputType::Result(_, _) => {
quote! { self.#field_ident(#ctx_param #(#use_keys),*).await? }
}
};
find_entities.push((
args.len(),
quote! {
if typename == &<#entity_type as #crate_name::Type>::type_name() {
if let (#(#key_pat),*) = (#(#key_getter),*) {
let ctx_obj = ctx.with_selection_set(&ctx.selection_set);
return #crate_name::OutputValueType::resolve(&#do_find, &ctx_obj, pos).await;
}
}
},
));
method.attrs.remove(
method
.attrs
.iter()
.enumerate()
.find(|(_, a)| a.path.is_ident("entity"))
.map(|(idx, _)| idx)
.unwrap(),
);
}
}
}

View File

@ -114,14 +114,14 @@ pub fn generate(object_args: &args::Object, input: &mut DeriveInput) -> Result<T
}
});
item.attrs.remove(
item.attrs
.iter()
.enumerate()
.find(|(_, a)| a.path.is_ident("field"))
.map(|(idx, _)| idx)
.unwrap(),
);
if let Some((idx, _)) = item
.attrs
.iter()
.enumerate()
.find(|(_, a)| a.path.is_ident("field"))
{
item.attrs.remove(idx);
}
}
}
}

View File

@ -17,12 +17,10 @@ struct MyObject {
#[Object]
impl MyObject {
#[field]
async fn value(&self) -> String {
self.value.to_string()
}
#[field]
async fn value_from_db(
&self,
ctx: &Context<'_'>,

View File

@ -2,19 +2,17 @@
简单对象是把Rust结构的所有字段都直接映射到GraphQL对象不支持定义单独的Resolve函数。
下面的例子定义了一个名称为MyObject的对象包含字段`a`和`b``c`由于没有`#[field]`标记所以不会映射到GraphQL。
下面的例子定义了一个名称为MyObject的对象包含字段`a`和`b``c`由于标记为`#[field(skip)]`所以不会映射到GraphQL。
```rust
use async_graphql::*;
#[SimpleObject]
struct MyObject {
#[field]
a: i32,
#[field]
b: i32,
#[field(skip)]
c: i32,
}
```

View File

@ -225,7 +225,6 @@ pub use types::{EnumItem, EnumType};
/// Ok(self.value)
/// }
///
/// #[field]
/// async fn value_with_arg(&self, #[arg(default = "1")] a: i32) -> i32 {
/// a
/// }
@ -283,7 +282,6 @@ pub use async_graphql_derive::Object;
///
/// #[SimpleObject]
/// struct QueryRoot {
/// #[field]
/// value: i32,
/// }
///
@ -462,19 +460,16 @@ pub use async_graphql_derive::InputObject;
/// #[Object]
/// impl TypeA {
/// /// Returns data borrowed from the context
/// #[field]
/// async fn value_a<'a>(&self, ctx: &'a Context<'_>) -> &'a str {
/// ctx.data::<String>().as_str()
/// }
///
/// /// Returns data borrowed self
/// #[field]
/// async fn value_b(&self) -> &i32 {
/// &self.value
/// }
///
/// /// With parameters
/// #[field]
/// async fn value_c(&self, a: i32, b: i32) -> i32 {
/// a + b
/// }
@ -493,7 +488,6 @@ pub use async_graphql_derive::InputObject;
///
/// #[Object]
/// impl QueryRoot {
/// #[field]
/// async fn type_a(&self) -> MyInterface {
/// TypeA { value: 10 }.into()
/// }
@ -571,7 +565,6 @@ pub use async_graphql_derive::Union;
///
/// #[Subscription]
/// impl SubscriptionRoot {
/// #[field]
/// async fn value(&self, event: &Event, condition: i32) -> bool {
/// // Push when value is greater than condition
/// event.value > condition

View File

@ -79,22 +79,18 @@ pub struct __Directive<'a> {
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."#
)]
impl<'a> __Directive<'a> {
#[field]
async fn name(&self) -> String {
self.directive.name.to_string()
}
#[field]
async fn description(&self) -> Option<String> {
self.directive.description.map(|s| s.to_string())
}
#[field]
async fn locations(&self) -> &Vec<__DirectiveLocation> {
&self.directive.locations
}
#[field]
async fn args(&self) -> Vec<__InputValue<'a>> {
self.directive
.args

View File

@ -11,22 +11,18 @@ pub struct __EnumValue<'a> {
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."
)]
impl<'a> __EnumValue<'a> {
#[field]
async fn name(&self, _: &Context<'_>) -> String {
self.value.name.to_string()
}
#[field]
async fn description(&self, _: &Context<'_>) -> Option<String> {
self.value.description.map(|s| s.to_string())
}
#[field]
async fn is_deprecated(&self, _: &Context<'_>) -> bool {
self.value.deprecation.is_some()
}
#[field]
async fn deprecation_reason(&self, _: &Context<'_>) -> Option<String> {
self.value.deprecation.map(|s| s.to_string())
}

View File

@ -13,17 +13,14 @@ pub struct __Field<'a> {
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."
)]
impl<'a> __Field<'a> {
#[field]
async fn name(&self) -> String {
self.field.name.to_string()
}
#[field]
async fn description(&self) -> Option<String> {
self.field.description.map(|s| s.to_string())
}
#[field]
async fn args(&self) -> Vec<__InputValue<'a>> {
let mut args = self
.field
@ -43,12 +40,10 @@ impl<'a> __Field<'a> {
__Type::new(self.registry, &self.field.ty)
}
#[field]
async fn is_deprecated(&self) -> bool {
self.field.deprecation.is_some()
}
#[field]
async fn deprecation_reason(&self) -> Option<String> {
self.field.deprecation.map(|s| s.to_string())
}

View File

@ -12,12 +12,10 @@ pub struct __InputValue<'a> {
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."
)]
impl<'a> __InputValue<'a> {
#[field]
async fn name(&self) -> String {
self.input_value.name.to_string()
}
#[field]
async fn description(&self) -> Option<String> {
self.input_value.description.map(|s| s.to_string())
}
@ -27,7 +25,6 @@ impl<'a> __InputValue<'a> {
__Type::new(self.registry, &self.input_value.ty)
}
#[field]
async fn default_value(&self) -> Option<String> {
self.input_value.default_value.map(|s| s.to_string())
}

View File

@ -49,7 +49,6 @@ Depending on the kind of a type, certain fields describe information about that
"#
)]
impl<'a> __Type<'a> {
#[field]
async fn kind(&self) -> __TypeKind {
match &self.detail {
TypeDetail::Named(ty) => match ty {
@ -65,7 +64,6 @@ impl<'a> __Type<'a> {
}
}
#[field]
async fn name(&self) -> Option<String> {
match &self.detail {
TypeDetail::Named(ty) => Some(ty.name().to_string()),
@ -74,7 +72,6 @@ impl<'a> __Type<'a> {
}
}
#[field]
async fn description(&self) -> Option<String> {
match &self.detail {
TypeDetail::Named(ty) => match ty {
@ -92,7 +89,6 @@ impl<'a> __Type<'a> {
}
}
#[field]
async fn fields(
&self,
#[arg(default = "false")] include_deprecated: bool,
@ -118,7 +114,6 @@ impl<'a> __Type<'a> {
}
}
#[field]
async fn interfaces(&self) -> Option<Vec<__Type<'a>>> {
if let TypeDetail::Named(registry::Type::Object { name, .. }) = &self.detail {
Some(
@ -135,7 +130,6 @@ impl<'a> __Type<'a> {
}
}
#[field]
async fn possible_types(&self) -> Option<Vec<__Type<'a>>> {
if let TypeDetail::Named(registry::Type::Interface { possible_types, .. }) = &self.detail {
Some(
@ -157,7 +151,6 @@ impl<'a> __Type<'a> {
}
}
#[field]
async fn enum_values(
&self,
#[arg(default = "false")] include_deprecated: bool,
@ -178,7 +171,6 @@ impl<'a> __Type<'a> {
}
}
#[field]
async fn input_fields(&self) -> Option<Vec<__InputValue<'a>>> {
if let TypeDetail::Named(registry::Type::InputObject { input_fields, .. }) = &self.detail {
Some(
@ -195,7 +187,6 @@ impl<'a> __Type<'a> {
}
}
#[field]
async fn of_type(&self) -> Option<__Type<'a>> {
if let TypeDetail::List(ty) = &self.detail {
Some(__Type::new(self.registry, &ty))

View File

@ -48,7 +48,6 @@ pub struct EmptyEdgeFields;
///
/// #[SimpleObject]
/// struct DiffFields {
/// #[field]
/// diff: i32,
/// }
///
@ -85,7 +84,6 @@ pub struct EmptyEdgeFields;
///
/// #[Object]
/// impl QueryRoot {
/// #[field]
/// async fn numbers(&self, ctx: &Context<'_>,
/// after: Option<String>,
/// before: Option<String>,

View File

@ -12,7 +12,6 @@ use std::collections::HashMap;
/// Federation service
#[SimpleObject(internal)]
struct Service {
#[field]
sdl: Option<String>,
}

View File

@ -22,7 +22,6 @@ use std::path::PathBuf;
///
/// #[async_graphql::Object]
/// impl MutationRoot {
/// #[field]
/// async fn upload(&self, file: Upload) -> bool {
/// println!("upload: filename={}", file.filename);
/// true

View File

@ -22,37 +22,30 @@ struct Dog;
#[Object(internal)]
impl Dog {
#[field]
async fn name(&self, surname: Option<bool>) -> Option<String> {
unimplemented!()
}
#[field]
async fn nickname(&self) -> Option<String> {
unimplemented!()
}
#[field]
async fn bark_volume(&self) -> Option<i32> {
unimplemented!()
}
#[field]
async fn barks(&self) -> Option<bool> {
unimplemented!()
}
#[field]
async fn does_know_command(&self, dog_command: Option<DogCommand>) -> Option<bool> {
unimplemented!()
}
#[field]
async fn is_housetrained(&self, #[arg(default = "true")] at_other_homes: bool) -> Option<bool> {
unimplemented!()
}
#[field]
async fn is_at_location(&self, x: Option<i32>, y: Option<i32>) -> Option<bool> {
unimplemented!()
}
@ -70,27 +63,22 @@ struct Cat;
#[Object(internal)]
impl Cat {
#[field]
async fn name(&self, surname: Option<bool>) -> Option<String> {
unimplemented!()
}
#[field]
async fn nickname(&self) -> Option<String> {
unimplemented!()
}
#[field]
async fn meows(&self) -> Option<bool> {
unimplemented!()
}
#[field]
async fn meow_volume(&self) -> Option<i32> {
unimplemented!()
}
#[field]
async fn fur_color(&self) -> Option<FurColor> {
unimplemented!()
}
@ -103,22 +91,18 @@ struct Human;
#[Object(internal)]
impl Human {
#[field]
async fn name(&self, surname: Option<bool>) -> Option<String> {
unimplemented!()
}
#[field]
async fn pets(&self) -> Option<Vec<Option<Pet>>> {
unimplemented!()
}
#[field]
async fn relatives(&self) -> Option<Vec<Human>> {
unimplemented!()
}
#[field]
async fn iq(&self) -> Option<i32> {
unimplemented!()
}
@ -128,17 +112,14 @@ struct Alien;
#[Object(internal)]
impl Alien {
#[field]
async fn name(&self, surname: Option<bool>) -> Option<String> {
unimplemented!()
}
#[field]
async fn iq(&self) -> Option<i32> {
unimplemented!()
}
#[field]
async fn num_eyes(&self) -> Option<i32> {
unimplemented!()
}
@ -185,19 +166,10 @@ struct Intelligent(Human, Alien);
#[InputObject(internal)]
struct ComplexInput {
#[field]
required_field: bool,
#[field]
int_field: Option<i32>,
#[field]
string_field: Option<String>,
#[field]
boolean_field: Option<bool>,
#[field]
string_list_field: Option<Vec<Option<String>>>,
}
@ -205,42 +177,34 @@ struct ComplicatedArgs;
#[Object(internal)]
impl ComplicatedArgs {
#[field]
async fn int_arg_field(&self, int_arg: Option<i32>) -> Option<String> {
unimplemented!()
}
#[field]
async fn non_null_int_arg_field(&self, non_null_int_arg: i32) -> Option<String> {
unimplemented!()
}
#[field]
async fn string_arg_field(&self, string_arg: Option<String>) -> Option<String> {
unimplemented!()
}
#[field]
async fn boolean_arg_field(&self, boolean_arg: Option<bool>) -> Option<String> {
unimplemented!()
}
#[field]
async fn enum_arg_field(&self, enum_arg: Option<FurColor>) -> Option<String> {
unimplemented!()
}
#[field]
async fn float_arg_field(&self, float_arg: Option<f64>) -> Option<String> {
unimplemented!()
}
#[field]
async fn id_arg_field(&self, id_arg: Option<ID>) -> Option<String> {
unimplemented!()
}
#[field]
async fn string_list_arg_field(
&self,
string_list_arg: Option<Vec<Option<String>>>,
@ -248,17 +212,14 @@ impl ComplicatedArgs {
unimplemented!()
}
#[field]
async fn complex_arg_field(&self, complex_arg: Option<ComplexInput>) -> Option<String> {
unimplemented!()
}
#[field]
async fn multiple_reqs(&self, req1: i32, req2: i32) -> Option<String> {
unimplemented!()
}
#[field]
async fn multiple_opts(
&self,
#[arg(default = "0")] opt1: i32,
@ -267,7 +228,6 @@ impl ComplicatedArgs {
unimplemented!()
}
#[field]
async fn multiple_opt_and_req(
&self,
req1: i32,
@ -283,57 +243,46 @@ pub struct QueryRoot;
#[Object(internal)]
impl QueryRoot {
#[field]
async fn human(&self, id: Option<ID>) -> Option<Human> {
unimplemented!()
}
#[field]
async fn alien(&self) -> Option<Alien> {
unimplemented!()
}
#[field]
async fn dog(&self) -> Option<Dog> {
unimplemented!()
}
#[field]
async fn cat(&self) -> Option<Cat> {
unimplemented!()
}
#[field]
async fn pet(&self) -> Option<Pet> {
unimplemented!()
}
#[field]
async fn being(&self) -> Option<Being> {
unimplemented!()
}
#[field]
async fn intelligent(&self) -> Option<Intelligent> {
unimplemented!()
}
#[field]
async fn cat_or_dog(&self) -> Option<CatOrDog> {
unimplemented!()
}
#[field]
async fn dog_or_human(&self) -> Option<DogOrHuman> {
unimplemented!()
}
#[field]
async fn human_or_alien(&self) -> Option<HumanOrAlien> {
unimplemented!()
}
#[field]
async fn complicated_args(&self) -> Option<ComplicatedArgs> {
unimplemented!()
}
@ -343,7 +292,6 @@ pub struct MutationRoot;
#[Object(internal)]
impl MutationRoot {
#[field]
async fn test_input(
&self,
#[arg(default = r#"{id: 423, name: "foo"}"#)] input: TestInput,

View File

@ -23,19 +23,16 @@ pub use string_validators::{Email, StringMaxLength, StringMinLength, MAC};
/// #[Object]
/// impl QueryRoot {
/// // Input is email address
/// #[field]
/// async fn value1(&self, #[arg(validator(Email))] email: String) -> i32 {
/// unimplemented!()
/// }
///
/// // Input is email or MAC address
/// #[field]
/// async fn value2(&self, #[arg(validator(or(Email, MAC(colon = false))))] email_or_mac: String) -> i32 {
/// unimplemented!()
/// }
///
/// // Input is integer between 100 and 200
/// #[field]
/// async fn value3(&self, #[arg(validator(IntRange(min = 100, max = 200)))] value: i32) -> i32 {
/// unimplemented!()
/// }

View File

@ -6,7 +6,6 @@ pub async fn test_directive_skip() {
#[Object]
impl QueryRoot {
#[field]
pub async fn value(&self) -> i32 {
10
}
@ -38,7 +37,6 @@ pub async fn test_directive_include() {
#[Object]
impl QueryRoot {
#[field]
pub async fn value(&self) -> i32 {
10
}

View File

@ -19,17 +19,14 @@ pub async fn test_enum_type() {
#[Object]
impl Root {
#[field]
async fn value(&self) -> MyEnum {
self.value
}
#[field]
async fn test_arg(&self, input: MyEnum) -> MyEnum {
input
}
#[field]
async fn test_input<'a>(&self, input: MyInput) -> MyEnum {
input.value
}

View File

@ -11,7 +11,6 @@ impl User {
&self.id
}
#[field]
async fn reviews(&self) -> Vec<Review> {
todo!()
}
@ -21,7 +20,6 @@ struct Review;
#[Object]
impl Review {
#[field]
async fn body(&self) -> String {
todo!()
}
@ -31,7 +29,6 @@ impl Review {
todo!()
}
#[field]
async fn product(&self) -> Product {
todo!()
}
@ -48,7 +45,6 @@ impl Product {
&self.upc
}
#[field]
async fn reviews(&self) -> Vec<Review> {
todo!()
}

View File

@ -30,27 +30,22 @@ pub async fn test_input_object_default_value() {
#[Object]
impl MyOutput {
#[field]
async fn a(&self) -> i32 {
self.a
}
#[field]
async fn b(&self) -> &Vec<i32> {
&self.b
}
#[field]
async fn c(&self) -> &String {
&self.c
}
#[field]
async fn d(&self) -> &Option<i32> {
&self.d
}
#[field]
async fn e(&self) -> &Option<i32> {
&self.e
}
@ -60,7 +55,6 @@ pub async fn test_input_object_default_value() {
#[Object]
impl Root {
#[field]
async fn a(&self, input: MyInput) -> MyOutput {
MyOutput {
a: input.a,

View File

@ -4,9 +4,7 @@ use async_graphql::*;
pub async fn test_interface_simple_object() {
#[async_graphql::SimpleObject]
struct MyObj {
#[field]
id: i32,
#[field]
title: String,
}
@ -17,7 +15,6 @@ pub async fn test_interface_simple_object() {
#[Object]
impl Query {
#[field]
async fn node(&self) -> Node {
MyObj {
id: 33,
@ -27,15 +24,13 @@ pub async fn test_interface_simple_object() {
}
}
let query = format!(
r#"{{
node {{
... on Node {{
let query = r#"{
node {
... on Node {
id
}}
}}
}}"#
);
}
}
}"#;
let schema = Schema::new(Query, EmptyMutation, EmptySubscription);
assert_eq!(
schema.execute(&query).await.unwrap().data,
@ -53,7 +48,6 @@ pub async fn test_interface_simple_object2() {
struct MyObj {
#[field(ref)]
id: i32,
#[field]
title: String,
}
@ -64,7 +58,6 @@ pub async fn test_interface_simple_object2() {
#[Object]
impl Query {
#[field]
async fn node(&self) -> Node {
MyObj {
id: 33,
@ -74,15 +67,13 @@ pub async fn test_interface_simple_object2() {
}
}
let query = format!(
r#"{{
node {{
... on Node {{
let query = r#"{
node {
... on Node {
id
}}
}}
}}"#
);
}
}
}"#;
let schema = Schema::new(Query, EmptyMutation, EmptySubscription);
assert_eq!(
schema.execute(&query).await.unwrap().data,
@ -100,17 +91,14 @@ pub async fn test_multiple_interfaces() {
#[async_graphql::Object]
impl MyObj {
#[field]
async fn value_a(&self) -> i32 {
1
}
#[field]
async fn value_b(&self) -> i32 {
2
}
#[field]
async fn value_c(&self) -> i32 {
3
}
@ -126,7 +114,6 @@ pub async fn test_multiple_interfaces() {
#[Object]
impl Query {
#[field]
async fn my_obj(&self) -> InterfaceB {
MyObj.into()
}
@ -135,21 +122,19 @@ pub async fn test_multiple_interfaces() {
let schema = Schema::build(Query, EmptyMutation, EmptySubscription)
.register_type::<InterfaceA>() // `InterfaceA` is not directly referenced, so manual registration is required.
.finish();
let query = format!(
r#"{{
myObj {{
... on InterfaceA {{
let query = r#"{
myObj {
... on InterfaceA {
valueA
}}
... on InterfaceB {{
}
... on InterfaceB {
valueB
}}
... on MyObj {{
}
... on MyObj {
valueC
}}
}}
}}"#
);
}
}
}"#;
assert_eq!(
schema.execute(&query).await.unwrap().data,
serde_json::json!({
@ -168,17 +153,14 @@ pub async fn test_multiple_objects_in_multiple_interfaces() {
#[async_graphql::Object]
impl MyObjOne {
#[field]
async fn value_a(&self) -> i32 {
1
}
#[field]
async fn value_b(&self) -> i32 {
2
}
#[field]
async fn value_c(&self) -> i32 {
3
}
@ -188,7 +170,6 @@ pub async fn test_multiple_objects_in_multiple_interfaces() {
#[async_graphql::Object]
impl MyObjTwo {
#[field]
async fn value_a(&self) -> i32 {
1
}
@ -204,7 +185,6 @@ pub async fn test_multiple_objects_in_multiple_interfaces() {
#[Object]
impl Query {
#[field]
async fn my_obj(&self) -> Vec<InterfaceA> {
vec![MyObjOne.into(), MyObjTwo.into()]
}
@ -213,21 +193,19 @@ pub async fn test_multiple_objects_in_multiple_interfaces() {
let schema = Schema::build(Query, EmptyMutation, EmptySubscription)
.register_type::<InterfaceB>() // `InterfaceB` is not directly referenced, so manual registration is required.
.finish();
let query = format!(
r#"{{
myObj {{
... on InterfaceA {{
let query = r#"{
myObj {
... on InterfaceA {
valueA
}}
... on InterfaceB {{
}
... on InterfaceB {
valueB
}}
... on MyObjOne {{
}
... on MyObjOne {
valueC
}}
}}
}}"#
);
}
}
}"#;
assert_eq!(
schema.execute(&query).await.unwrap().data,
serde_json::json!({
@ -248,7 +226,6 @@ pub async fn test_interface_field_result() {
#[async_graphql::Object]
impl MyObj {
#[field]
async fn value(&self) -> FieldResult<i32> {
Ok(10)
}
@ -261,21 +238,18 @@ pub async fn test_interface_field_result() {
#[Object]
impl Query {
#[field]
async fn node(&self) -> Node {
MyObj.into()
}
}
let query = format!(
r#"{{
node {{
... on Node {{
let query = r#"{
node {
... on Node {
value
}}
}}
}}"#
);
}
}
}"#;
let schema = Schema::new(Query, EmptyMutation, EmptySubscription);
assert_eq!(
schema.execute(&query).await.unwrap().data,

View File

@ -13,27 +13,22 @@ pub async fn test_list_type() {
#[Object]
impl Root {
#[field]
async fn value_vec(&self) -> Vec<i32> {
self.value.clone()
}
#[field]
async fn value_slice(&self) -> &[i32] {
&self.value
}
#[field]
async fn value_input_slice(&self, a: Vec<i32>) -> Vec<i32> {
a
}
#[field]
async fn test_arg(&self, input: Vec<i32>) -> Vec<i32> {
input
}
#[field]
async fn test_input<'a>(&self, input: MyInput) -> Vec<i32> {
input.value
}

View File

@ -14,14 +14,12 @@ pub async fn test_mutation_execution_order() {
#[Object]
impl MutationRoot {
#[field]
async fn append1(&self, ctx: &Context<'_>) -> bool {
async_std::task::sleep(Duration::from_secs(1)).await;
ctx.data::<List>().lock().await.push(1);
true
}
#[field]
async fn append2(&self, ctx: &Context<'_>) -> bool {
async_std::task::sleep(Duration::from_millis(500)).await;
ctx.data::<List>().lock().await.push(2);
@ -50,7 +48,6 @@ pub async fn test_mutation_fragment() {
#[Object]
impl MutationRoot {
#[field]
async fn action(&self) -> bool {
true
}

View File

@ -14,32 +14,26 @@ pub async fn test_optional_type() {
#[Object]
impl Root {
#[field]
async fn value1(&self) -> Option<i32> {
self.value1.clone()
}
#[field]
async fn value1_ref(&self) -> &Option<i32> {
&self.value1
}
#[field]
async fn value2(&self) -> Option<i32> {
self.value2.clone()
}
#[field]
async fn value2_ref(&self) -> &Option<i32> {
&self.value2
}
#[field]
async fn test_arg(&self, input: Option<i32>) -> Option<i32> {
input
}
#[field]
async fn test_input<'a>(&self, input: MyInput) -> Option<i32> {
input.value.clone()
}

View File

@ -15,17 +15,14 @@ macro_rules! test_scalars {
#[Object]
impl Root {
#[field]
async fn value(&self) -> $ty {
self.value
}
#[field]
async fn test_arg(&self, input: $ty) -> $ty {
input
}
#[field]
async fn test_input(&self, input: MyInput) -> $ty {
input.value
}

View File

@ -8,10 +8,7 @@ pub async fn test_subscription() {
#[SimpleObject]
struct Event {
#[field]
a: i32,
#[field]
b: i32,
}
@ -22,12 +19,10 @@ pub async fn test_subscription() {
#[Subscription]
impl SubscriptionRoot {
#[field]
async fn values(&self, start: i32, end: i32) -> impl Stream<Item = i32> {
futures::stream::iter(start..end)
}
#[field]
async fn events(&self, start: i32, end: i32) -> impl Stream<Item = Event> {
futures::stream::iter((start..end).map(|n| Event { a: n, b: n * 10 }))
}
@ -81,14 +76,12 @@ pub async fn test_simple_broker() {
#[SimpleObject]
#[derive(Clone)]
struct Event1 {
#[field]
value: i32,
}
#[SimpleObject]
#[derive(Clone)]
struct Event2 {
#[field]
value: i32,
}
@ -99,12 +92,10 @@ pub async fn test_simple_broker() {
#[Subscription]
impl SubscriptionRoot {
#[field]
async fn events1(&self) -> impl Stream<Item = Event1> {
SimpleBroker::<Event1>::subscribe()
}
#[field]
async fn events2(&self) -> impl Stream<Item = Event2> {
SimpleBroker::<Event2>::subscribe()
}
@ -165,7 +156,6 @@ pub async fn test_subscription_with_ctx_data() {
#[Object]
impl MyObject {
#[field]
async fn value(&self, ctx: &Context<'_>) -> i32 {
*ctx.data::<i32>()
}
@ -175,13 +165,11 @@ pub async fn test_subscription_with_ctx_data() {
#[Subscription]
impl SubscriptionRoot {
#[field]
async fn values(&self, ctx: &Context<'_>) -> impl Stream<Item = i32> {
let value = *ctx.data::<i32>();
futures::stream::once(async move { value })
}
#[field]
async fn objects(&self) -> impl Stream<Item = MyObject> {
futures::stream::once(async move { MyObject })
}
@ -228,7 +216,6 @@ pub async fn test_subscription_with_token() {
#[Subscription]
impl SubscriptionRoot {
#[field]
async fn values(&self, ctx: &Context<'_>) -> FieldResult<impl Stream<Item = i32>> {
if ctx.data::<Token>().0 != "123456" {
return Err("forbidden".into());
@ -288,7 +275,6 @@ pub async fn test_subscription_ws_transport() {
#[Subscription]
impl SubscriptionRoot {
#[field]
async fn values(&self) -> impl Stream<Item = i32> {
futures::stream::iter(0..10)
}
@ -354,7 +340,6 @@ pub async fn test_subscription_ws_transport_with_token() {
#[Subscription]
impl SubscriptionRoot {
#[field]
async fn values(&self, ctx: &Context<'_>) -> FieldResult<impl Stream<Item = i32>> {
if ctx.data::<Token>().0 != "123456" {
return Err("forbidden".into());

View File

@ -6,12 +6,10 @@ pub async fn test_variables() {
#[Object]
impl QueryRoot {
#[field]
pub async fn int_val(&self, value: i32) -> i32 {
value
}
#[field]
pub async fn int_list_val(&self, value: Vec<i32>) -> Vec<i32> {
value
}
@ -49,7 +47,6 @@ pub async fn test_variable_default_value() {
#[Object]
impl QueryRoot {
#[field]
pub async fn int_val(&self, value: i32) -> i32 {
value
}