feat: add with for object & complex object & update documentation

This commit is contained in:
Miaxos 2021-10-28 12:42:13 +00:00
parent f3ef60033f
commit b361119ca0
5 changed files with 152 additions and 20 deletions

View File

@ -40,6 +40,7 @@ pub fn generate(
if derived.name.is_some() && derived.into.is_some() {
let base_function_name = &method.sig.ident;
let name = derived.name.unwrap();
let with = derived.with;
let into = Type::Verbatim(
proc_macro2::TokenStream::from_str(&derived.into.unwrap()).unwrap(),
);
@ -93,11 +94,16 @@ pub fn generate(
.into_iter(),
);
let new_block = quote!({
{
::std::result::Result::Ok(#self_ty::#base_function_name(&self, #other_atts).await?.into())
}
});
let new_block = match with {
Some(with) => quote!({
::std::result::Result::Ok(#with(#self_ty::#base_function_name(&self, #other_atts).await?))
}),
None => quote!({
{
::std::result::Result::Ok(#self_ty::#base_function_name(&self, #other_atts).await?.into())
}
}),
};
new_impl.block = syn::parse2::<Block>(new_block).expect("invalid block");

View File

@ -55,6 +55,7 @@ pub fn generate(
if derived.name.is_some() && derived.into.is_some() {
let base_function_name = &method.sig.ident;
let name = derived.name.unwrap();
let with = derived.with;
let into = Type::Verbatim(
proc_macro2::TokenStream::from_str(&derived.into.unwrap()).unwrap(),
);
@ -108,11 +109,16 @@ pub fn generate(
.into_iter(),
);
let new_block = quote!({
{
::std::result::Result::Ok(#self_ty::#base_function_name(&self, #other_atts).await?.into())
}
});
let new_block = match with {
Some(with) => quote!({
::std::result::Result::Ok(#with(#self_ty::#base_function_name(&self, #other_atts).await?))
}),
None => quote!({
{
::std::result::Result::Ok(#self_ty::#base_function_name(&self, #other_atts).await?.into())
}
}),
};
new_impl.block = syn::parse2::<Block>(new_block).expect("invalid block");

View File

@ -69,9 +69,9 @@ impl From<Vec<U>> for Vec<T> {
}
```
So you wouldn't be able to generate derived fields for existing wrapper type structures like `Vec` or `Option`. But when you implement a `From<U> for T` you should be able to derived a `From<Vec<U>> for Vec<T>` and a `From<Option<U>> for Option<T>`, so a coercion mecanism has been included so you'll be able to use the derived macro argument with `Vec` and `Option`.
So you wouldn't be able to generate derived fields for existing wrapper type structures like `Vec` or `Option`. But when you implement a `From<U> for T` you should be able to derived a `From<Vec<U>> for Vec<T>` and a `From<Option<U>> for Option<T>`.
We included a `with` parameter to help you define a function to call instead of using the `Into` trait implementation between wrapper structures.
This coercion mecanism impose these derived to be `owned`.
### Example
@ -91,15 +91,13 @@ impl From<ValueDerived> for ValueDerived2 {
}
}
fn option_to_option<T, U: From<T>>(value: Option<T>) -> Option<U> {
value.map(|x| x.into())
}
#[derive(SimpleObject)]
struct TestObj {
#[graphql(derived(owned, name = "value2", into = "Option<ValueDerived2>"))]
#[graphql(derived(owned, name = "value2", into = "Option<ValueDerived2>", with = "option_to_option"))]
pub value1: Option<ValueDerived>,
#[graphql(derived(owned, name = "value_vec_2", into = "Vec<ValueDerived2>"))]
pub value_vec_1: Vec<ValueDerived>,
#[graphql(derived(owned, name = "value_opt_vec_2", into = "Option<Vec<ValueDerived2>>"))]
pub value_opt_vec_1: Option<Vec<ValueDerived>>,
#[graphql(derived(owned, name = "value_vec_opt_2", into = "Vec<Option<ValueDerived2>>"))]
pub value_vec_opt_1: Vec<Option<ValueDerived>>,
}
```

View File

@ -295,6 +295,7 @@ pub type FieldResult<T> = Result<T>;
/// |--------------|------------------------------------------|------------ |----------|
/// | name | Generated derived field name | string | N |
/// | into | Type to derived an into | string | Y |
/// | with | Function to apply to manage advanced use cases | string| Y |
///
/// # Valid field return types
///
@ -467,8 +468,8 @@ pub use async_graphql_derive::Object;
/// |--------------|------------------------------------------|------------ |----------|
/// | name | Generated derived field name | string | N |
/// | into | Type to derived an into | string | Y |
/// | into | Type to derived an into | string | Y |
/// | owned | Field resolver return a ownedship value | bool | Y |
/// | with | Function to apply to manage advanced use cases | string| Y |
///
///
/// # Examples
@ -533,6 +534,7 @@ pub use async_graphql_derive::SimpleObject;
/// |--------------|------------------------------------------|------------ |----------|
/// | name | Generated derived field name | string | N |
/// | into | Type to derived an into | string | Y |
/// | with | Function to apply to manage advanced use cases | string| Y |
///
/// # Examples
///

View File

@ -46,6 +46,60 @@ pub async fn test_derived_field_object() {
);
}
#[tokio::test]
pub async fn test_derived_field_object_with() {
use serde::{Deserialize, Serialize};
struct Query;
#[derive(Serialize, Deserialize)]
struct ValueDerived(String);
scalar!(ValueDerived);
impl From<i32> for ValueDerived {
fn from(value: i32) -> Self {
ValueDerived(format!("{}", value))
}
}
fn option_to_option<T, U: From<T>>(value: Option<T>) -> Option<U> {
value.map(|x| x.into())
}
#[Object]
impl Query {
#[graphql(derived(
name = "value2",
into = "Option<ValueDerived>",
with = "option_to_option"
))]
async fn value1(&self, #[graphql(default = 100)] input: i32) -> Option<i32> {
Some(input)
}
}
let query = "{ value1 value2 }";
let schema = Schema::new(Query, EmptyMutation, EmptySubscription);
assert_eq!(
schema.execute(query).await.data,
value!({
"value1": 100,
"value2": "100",
})
);
let query = "{ value1(input: 1) value2(input: 2) }";
let schema = Schema::new(Query, EmptyMutation, EmptySubscription);
assert_eq!(
schema.execute(query).await.data,
value!({
"value1": 1,
"value2": "2",
})
);
}
#[tokio::test]
pub async fn test_derived_field_simple_object() {
use serde::{Deserialize, Serialize};
@ -261,3 +315,69 @@ pub async fn test_derived_field_complex_object() {
})
);
}
#[tokio::test]
pub async fn test_derived_field_complex_object_derived() {
use serde::{Deserialize, Serialize};
#[derive(SimpleObject)]
#[graphql(complex)]
struct MyObj {
a: i32,
#[graphql(owned, derived(name = "f", into = "ValueDerived"))]
b: i32,
}
#[derive(Serialize, Deserialize)]
struct ValueDerived(String);
scalar!(ValueDerived);
impl From<i32> for ValueDerived {
fn from(value: i32) -> Self {
ValueDerived(format!("{}", value))
}
}
fn option_to_option<T, U: From<T>>(value: Option<T>) -> Option<U> {
value.map(|x| x.into())
}
#[ComplexObject]
impl MyObj {
async fn c(&self) -> i32 {
self.a + self.b
}
#[graphql(derived(name = "e", into = "Option<ValueDerived>", with = "option_to_option"))]
async fn d(&self, v: i32) -> Option<i32> {
Some(self.a + self.b + v)
}
}
struct Query;
#[Object]
impl Query {
async fn obj(&self) -> MyObj {
MyObj { a: 10, b: 20 }
}
}
let query = "{ obj { a b c d(v:100) e(v: 200) f } }";
let schema = Schema::new(Query, EmptyMutation, EmptySubscription);
dbg!(schema.execute(query).await);
assert_eq!(
schema.execute(query).await.data,
value!({
"obj": {
"a": 10,
"b": 20,
"c": 30,
"d": 130,
"e": "230",
"f": "20",
},
})
);
}