Add NewType
derive macro. #388
This commit is contained in:
parent
8ee82b3d6e
commit
66aadd6319
|
@ -542,3 +542,13 @@ pub struct Description {
|
|||
#[darling(default)]
|
||||
pub internal: bool,
|
||||
}
|
||||
|
||||
#[derive(FromDeriveInput)]
|
||||
pub struct NewType {
|
||||
pub ident: Ident,
|
||||
pub generics: Generics,
|
||||
pub data: Data<Ignored, syn::Type>,
|
||||
|
||||
#[darling(default)]
|
||||
pub internal: bool,
|
||||
}
|
||||
|
|
|
@ -7,11 +7,10 @@ use crate::utils::{get_crate_name, get_rustdoc, GeneratorResult};
|
|||
pub fn generate(desc_args: &args::Description) -> GeneratorResult<TokenStream> {
|
||||
let crate_name = get_crate_name(desc_args.internal);
|
||||
let ident = &desc_args.ident;
|
||||
let generics = &desc_args.generics;
|
||||
let where_clause = &generics.where_clause;
|
||||
let (impl_generics, ty_generics, where_clause) = desc_args.generics.split_for_impl();
|
||||
let doc = get_rustdoc(&desc_args.attrs)?.unwrap_or_default();
|
||||
let expanded = quote! {
|
||||
impl #generics #crate_name::Description for #ident #generics #where_clause {
|
||||
impl #impl_generics #crate_name::Description for #ident #ty_generics #where_clause {
|
||||
fn description() -> &'static str {
|
||||
#doc
|
||||
}
|
||||
|
|
|
@ -10,6 +10,7 @@ mod input_object;
|
|||
mod interface;
|
||||
mod merged_object;
|
||||
mod merged_subscription;
|
||||
mod newtype;
|
||||
mod object;
|
||||
mod output_type;
|
||||
mod scalar;
|
||||
|
@ -161,7 +162,7 @@ pub fn derive_merged_subscription(input: TokenStream) -> TokenStream {
|
|||
pub fn derive_merged_description(input: TokenStream) -> TokenStream {
|
||||
let desc_args =
|
||||
match args::Description::from_derive_input(&parse_macro_input!(input as DeriveInput)) {
|
||||
Ok(object_args) => object_args,
|
||||
Ok(desc_args) => desc_args,
|
||||
Err(err) => return TokenStream::from(err.write_errors()),
|
||||
};
|
||||
match description::generate(&desc_args) {
|
||||
|
@ -169,3 +170,16 @@ pub fn derive_merged_description(input: TokenStream) -> TokenStream {
|
|||
Err(err) => err.write_errors().into(),
|
||||
}
|
||||
}
|
||||
|
||||
#[proc_macro_derive(NewType)]
|
||||
pub fn derive_newtype(input: TokenStream) -> TokenStream {
|
||||
let newtype_args =
|
||||
match args::NewType::from_derive_input(&parse_macro_input!(input as DeriveInput)) {
|
||||
Ok(newtype_args) => newtype_args,
|
||||
Err(err) => return TokenStream::from(err.write_errors()),
|
||||
};
|
||||
match newtype::generate(&newtype_args) {
|
||||
Ok(expanded) => expanded,
|
||||
Err(err) => err.write_errors().into(),
|
||||
}
|
||||
}
|
||||
|
|
76
derive/src/newtype.rs
Normal file
76
derive/src/newtype.rs
Normal file
|
@ -0,0 +1,76 @@
|
|||
use darling::ast::{Data, Style};
|
||||
use proc_macro::TokenStream;
|
||||
use quote::quote;
|
||||
use syn::Error;
|
||||
|
||||
use crate::args;
|
||||
use crate::utils::{get_crate_name, GeneratorResult};
|
||||
|
||||
pub fn generate(newtype_args: &args::NewType) -> GeneratorResult<TokenStream> {
|
||||
let crate_name = get_crate_name(newtype_args.internal);
|
||||
let ident = &newtype_args.ident;
|
||||
let (impl_generics, ty_generics, where_clause) = newtype_args.generics.split_for_impl();
|
||||
|
||||
let fields = match &newtype_args.data {
|
||||
Data::Struct(e) => e,
|
||||
_ => {
|
||||
return Err(
|
||||
Error::new_spanned(ident, "NewType can only be applied to an struct.").into(),
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
if fields.style == Style::Tuple && fields.fields.len() != 1 {
|
||||
return Err(Error::new_spanned(ident, "Invalid type.").into());
|
||||
}
|
||||
let inner_ty = &fields.fields[0];
|
||||
|
||||
let expanded = quote! {
|
||||
#[allow(clippy::all, clippy::pedantic)]
|
||||
impl #impl_generics #crate_name::ScalarType for #ident #ty_generics #where_clause {
|
||||
fn parse(value: #crate_name::Value) -> #crate_name::InputValueResult<Self> {
|
||||
<#inner_ty as #crate_name::ScalarType>::parse(value).map(#ident).map_err(#crate_name::InputValueError::propagate)
|
||||
}
|
||||
|
||||
fn to_value(&self) -> #crate_name::Value {
|
||||
<#inner_ty as #crate_name::ScalarType>::to_value(&self.0)
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::all, clippy::pedantic)]
|
||||
impl #impl_generics #crate_name::Type for #ident #ty_generics #where_clause {
|
||||
fn type_name() -> ::std::borrow::Cow<'static, ::std::primitive::str> {
|
||||
<#inner_ty as #crate_name::Type>::type_name()
|
||||
}
|
||||
|
||||
fn create_type_info(registry: &mut #crate_name::registry::Registry) -> ::std::string::String {
|
||||
<#inner_ty as #crate_name::Type>::create_type_info(registry)
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::all, clippy::pedantic)]
|
||||
impl #impl_generics #crate_name::InputType for #ident #ty_generics #where_clause {
|
||||
fn parse(value: ::std::option::Option<#crate_name::Value>) -> #crate_name::InputValueResult<Self> {
|
||||
<#ident as #crate_name::ScalarType>::parse(value.unwrap_or_default())
|
||||
}
|
||||
|
||||
fn to_value(&self) -> #crate_name::Value {
|
||||
<#ident as #crate_name::ScalarType>::to_value(self)
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::all, clippy::pedantic)]
|
||||
#[#crate_name::async_trait::async_trait]
|
||||
impl #impl_generics #crate_name::OutputType for #ident #ty_generics #where_clause {
|
||||
async fn resolve(
|
||||
&self,
|
||||
_: &#crate_name::ContextSelectionSet<'_>,
|
||||
_field: &#crate_name::Positioned<#crate_name::parser::types::Field>
|
||||
) -> #crate_name::ServerResult<#crate_name::Value> {
|
||||
::std::result::Result::Ok(#crate_name::ScalarType::to_value(self))
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Ok(expanded.into())
|
||||
}
|
55
src/lib.rs
55
src/lib.rs
|
@ -874,6 +874,61 @@ pub use async_graphql_derive::Subscription;
|
|||
///
|
||||
pub use async_graphql_derive::Scalar;
|
||||
|
||||
/// Define a NewType Scalar
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// use async_graphql::*;
|
||||
///
|
||||
/// #[derive(NewType)]
|
||||
/// struct Weight(f64);
|
||||
///
|
||||
/// struct QueryRoot;
|
||||
///
|
||||
/// #[Object]
|
||||
/// impl QueryRoot {
|
||||
/// async fn value(&self) -> Weight {
|
||||
/// Weight(1.234)
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// async_std::task::block_on(async move {
|
||||
/// let schema = Schema::build(QueryRoot, EmptyMutation, EmptySubscription).data("hello".to_string()).finish();
|
||||
///
|
||||
/// let res = schema.execute("{ value }").await.into_result().unwrap().data;
|
||||
/// assert_eq!(res, value!({
|
||||
/// "value": 1.234,
|
||||
/// }));
|
||||
///
|
||||
/// let res = schema.execute(r#"
|
||||
/// {
|
||||
/// __type(name: "QueryRoot") {
|
||||
/// fields {
|
||||
/// name type {
|
||||
/// kind
|
||||
/// ofType { name }
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
/// }"#).await.into_result().unwrap().data;
|
||||
/// assert_eq!(res, value!({
|
||||
/// "__type": {
|
||||
/// "fields": [{
|
||||
/// "name": "value",
|
||||
/// "type": {
|
||||
/// "kind": "NON_NULL",
|
||||
/// "ofType": {
|
||||
/// "name": "Float"
|
||||
/// }
|
||||
/// }
|
||||
/// }]
|
||||
/// }
|
||||
/// }));
|
||||
/// });
|
||||
/// ```
|
||||
pub use async_graphql_derive::NewType;
|
||||
|
||||
/// Define a merged object with multiple object types.
|
||||
///
|
||||
/// *[See also the Book](https://async-graphql.github.io/async-graphql/en/merging_objects.html).*
|
||||
|
|
Loading…
Reference in New Issue
Block a user