Add `name` and `visible` attributes for `Newtype` macro for define a new scalar. #437
This commit is contained in:
parent
662454c103
commit
37cacf64dc
|
@ -6,7 +6,14 @@ nd this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.
|
|||
|
||||
## [Unreleased]
|
||||
|
||||
## Removed
|
||||
|
||||
- Remove `SchemaBuilder::override_name` method. [#437](https://github.com/async-graphql/async-graphql/issues/437)
|
||||
|
||||
## Added
|
||||
|
||||
- Add `name` and `visible` attributes for `Newtype` macro for define a new scalar. [#437](https://github.com/async-graphql/async-graphql/issues/437)
|
||||
- `NewType` macro now also implements `From<InnerType>` and `Into<InnerType>`.
|
||||
|
||||
## [2.7.1]
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@ use darling::ast::{Data, Fields};
|
|||
use darling::util::Ignored;
|
||||
use darling::{FromDeriveInput, FromField, FromMeta, FromVariant};
|
||||
use inflector::Inflector;
|
||||
use std::prelude::v1::Result::Err;
|
||||
use syn::{
|
||||
Attribute, Generics, Ident, Lit, LitBool, LitStr, Meta, NestedMeta, Path, Type, Visibility,
|
||||
};
|
||||
|
@ -577,14 +578,51 @@ pub struct Description {
|
|||
pub internal: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum NewTypeName {
|
||||
UseNewName(String),
|
||||
UseRustName,
|
||||
UseOriginalName,
|
||||
}
|
||||
|
||||
impl Default for NewTypeName {
|
||||
fn default() -> Self {
|
||||
Self::UseOriginalName
|
||||
}
|
||||
}
|
||||
|
||||
impl FromMeta for NewTypeName {
|
||||
fn from_word() -> darling::Result<Self> {
|
||||
Ok(Self::UseRustName)
|
||||
}
|
||||
|
||||
fn from_string(value: &str) -> darling::Result<Self> {
|
||||
Ok(Self::UseNewName(value.to_string()))
|
||||
}
|
||||
|
||||
fn from_bool(value: bool) -> darling::Result<Self> {
|
||||
if value {
|
||||
Ok(Self::UseRustName)
|
||||
} else {
|
||||
Ok(Self::UseOriginalName)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(FromDeriveInput)]
|
||||
#[darling(attributes(graphql), forward_attrs(doc))]
|
||||
pub struct NewType {
|
||||
pub ident: Ident,
|
||||
pub generics: Generics,
|
||||
pub attrs: Vec<Attribute>,
|
||||
pub data: Data<Ignored, syn::Type>,
|
||||
|
||||
#[darling(default)]
|
||||
pub internal: bool,
|
||||
#[darling(default)]
|
||||
pub name: NewTypeName,
|
||||
#[darling(default)]
|
||||
pub visible: Option<Visible>,
|
||||
}
|
||||
|
||||
#[derive(FromMeta, Default)]
|
||||
|
|
|
@ -175,8 +175,8 @@ pub fn derive_merged_subscription(input: TokenStream) -> TokenStream {
|
|||
}
|
||||
}
|
||||
|
||||
#[proc_macro_derive(Description)]
|
||||
pub fn derive_merged_description(input: TokenStream) -> TokenStream {
|
||||
#[proc_macro_derive(Description, attributes(graphql))]
|
||||
pub fn derive_description(input: TokenStream) -> TokenStream {
|
||||
let desc_args =
|
||||
match args::Description::from_derive_input(&parse_macro_input!(input as DeriveInput)) {
|
||||
Ok(desc_args) => desc_args,
|
||||
|
@ -188,7 +188,7 @@ pub fn derive_merged_description(input: TokenStream) -> TokenStream {
|
|||
}
|
||||
}
|
||||
|
||||
#[proc_macro_derive(NewType)]
|
||||
#[proc_macro_derive(NewType, attributes(graphql))]
|
||||
pub fn derive_newtype(input: TokenStream) -> TokenStream {
|
||||
let newtype_args =
|
||||
match args::NewType::from_derive_input(&parse_macro_input!(input as DeriveInput)) {
|
||||
|
|
|
@ -3,13 +3,22 @@ use proc_macro::TokenStream;
|
|||
use quote::quote;
|
||||
use syn::Error;
|
||||
|
||||
use crate::args;
|
||||
use crate::utils::{get_crate_name, GeneratorResult};
|
||||
use crate::args::{self, NewTypeName, RenameTarget};
|
||||
use crate::utils::{get_crate_name, get_rustdoc, visible_fn, 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 gql_typename = match &newtype_args.name {
|
||||
NewTypeName::UseNewName(name) => Some(name.clone()),
|
||||
NewTypeName::UseRustName => Some(RenameTarget::Type.rename(ident.to_string())),
|
||||
NewTypeName::UseOriginalName => None,
|
||||
};
|
||||
let desc = get_rustdoc(&newtype_args.attrs)?
|
||||
.map(|s| quote! { ::std::option::Option::Some(#s) })
|
||||
.unwrap_or_else(|| quote! {::std::option::Option::None});
|
||||
let visible = visible_fn(&newtype_args.visible);
|
||||
|
||||
let fields = match &newtype_args.data {
|
||||
Data::Struct(e) => e,
|
||||
|
@ -24,6 +33,22 @@ pub fn generate(newtype_args: &args::NewType) -> GeneratorResult<TokenStream> {
|
|||
return Err(Error::new_spanned(ident, "Invalid type.").into());
|
||||
}
|
||||
let inner_ty = &fields.fields[0];
|
||||
let type_name = match &gql_typename {
|
||||
Some(name) => quote! { ::std::borrow::Cow::Borrowed(#name) },
|
||||
None => quote! { <#inner_ty as #crate_name::Type>::type_name() },
|
||||
};
|
||||
let create_type_info = if let Some(name) = &gql_typename {
|
||||
quote! {
|
||||
registry.create_type::<#ident, _>(|_| #crate_name::registry::MetaType::Scalar {
|
||||
name: ::std::borrow::ToOwned::to_owned(#name),
|
||||
description: #desc,
|
||||
is_valid: |value| <#ident as #crate_name::ScalarType>::is_valid(value),
|
||||
visible: #visible,
|
||||
})
|
||||
}
|
||||
} else {
|
||||
quote! { <#inner_ty as #crate_name::Type>::create_type_info(registry) }
|
||||
};
|
||||
|
||||
let expanded = quote! {
|
||||
#[allow(clippy::all, clippy::pedantic)]
|
||||
|
@ -37,14 +62,26 @@ pub fn generate(newtype_args: &args::NewType) -> GeneratorResult<TokenStream> {
|
|||
}
|
||||
}
|
||||
|
||||
impl #impl_generics ::std::convert::From<#inner_ty> for #ident #ty_generics #where_clause {
|
||||
fn from(value: #inner_ty) -> Self {
|
||||
Self(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl #impl_generics ::std::convert::Into<#inner_ty> for #ident #ty_generics #where_clause {
|
||||
fn into(self) -> #inner_ty {
|
||||
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()
|
||||
#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)
|
||||
#create_type_info
|
||||
}
|
||||
}
|
||||
|
||||
|
|
80
src/lib.rs
80
src/lib.rs
|
@ -952,8 +952,21 @@ pub use async_graphql_derive::Scalar;
|
|||
|
||||
/// Define a NewType Scalar
|
||||
///
|
||||
/// It also implements `From<InnerType>` and `Into<InnerType>`.
|
||||
///
|
||||
/// # Macro parameters
|
||||
///
|
||||
/// | Attribute | description | Type | Optional |
|
||||
/// |-------------|---------------------------|----------|----------|
|
||||
/// | name | If this attribute is provided then define a new scalar, otherwise it is just a transparent proxy for the internal scalar. | string | Y |
|
||||
/// | name | If this attribute is provided then define a new scalar, otherwise it is just a transparent proxy for the internal scalar. | bool | Y |
|
||||
/// | visible(Only valid for new scalars.) | If `false`, it will not be displayed in introspection. *[See also the Book](https://async-graphql.github.io/async-graphql/en/visibility.html).* | bool | Y |
|
||||
/// | visible(Only valid for new scalars.) | Call the specified function. If the return value is `false`, it will not be displayed in introspection. | string | Y |
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ## Use the original scalar name
|
||||
///
|
||||
/// ```rust
|
||||
/// use async_graphql::*;
|
||||
///
|
||||
|
@ -969,6 +982,64 @@ pub use async_graphql_derive::Scalar;
|
|||
/// }
|
||||
/// }
|
||||
///
|
||||
/// // Test conversion
|
||||
/// let weight: Weight = 10f64.into();
|
||||
/// let weight_f64: f64 = weight.into();
|
||||
///
|
||||
/// tokio::runtime::Runtime::new().unwrap().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"
|
||||
/// }
|
||||
/// }
|
||||
/// }]
|
||||
/// }
|
||||
/// }));
|
||||
/// });
|
||||
/// ```
|
||||
///
|
||||
/// ## Define a new scalar
|
||||
///
|
||||
/// ```rust
|
||||
/// use async_graphql::*;
|
||||
///
|
||||
/// /// Widget NewType
|
||||
/// #[derive(NewType)]
|
||||
/// #[graphql(name)] // or: #[graphql(name = true)], #[graphql(name = "Weight")]
|
||||
/// struct Weight(f64);
|
||||
///
|
||||
/// struct QueryRoot;
|
||||
///
|
||||
/// #[Object]
|
||||
/// impl QueryRoot {
|
||||
/// async fn value(&self) -> Weight {
|
||||
/// Weight(1.234)
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// tokio::runtime::Runtime::new().unwrap().block_on(async move {
|
||||
/// let schema = Schema::build(QueryRoot, EmptyMutation, EmptySubscription).data("hello".to_string()).finish();
|
||||
///
|
||||
|
@ -995,12 +1066,19 @@ pub use async_graphql_derive::Scalar;
|
|||
/// "type": {
|
||||
/// "kind": "NON_NULL",
|
||||
/// "ofType": {
|
||||
/// "name": "Float"
|
||||
/// "name": "Weight"
|
||||
/// }
|
||||
/// }
|
||||
/// }]
|
||||
/// }
|
||||
/// }));
|
||||
///
|
||||
/// assert_eq!(schema.execute(r#"{ __type(name: "Weight") { name description } }"#).
|
||||
/// await.into_result().unwrap().data, value!({
|
||||
/// "__type": {
|
||||
/// "name": "Weight", "description": "Widget NewType"
|
||||
/// }
|
||||
/// }));
|
||||
/// });
|
||||
/// ```
|
||||
pub use async_graphql_derive::NewType;
|
||||
|
|
Loading…
Reference in New Issue