Add remote attribute for Enum macro. #276

This commit is contained in:
Sunli 2020-09-27 16:05:25 +08:00
parent 0d6771a199
commit a725594cd1
3 changed files with 81 additions and 7 deletions

View File

@ -326,6 +326,7 @@ pub struct Enum {
pub internal: bool,
pub name: Option<String>,
pub desc: Option<String>,
pub remote: Option<String>,
}
impl Enum {
@ -333,6 +334,7 @@ impl Enum {
let mut internal = false;
let mut name = None;
let mut desc = None;
let mut remote = None;
for arg in args {
match arg {
@ -358,6 +360,15 @@ impl Enum {
"Attribute 'desc' should be a string.",
));
}
} else if nv.path.is_ident("remote") {
if let syn::Lit::Str(lit) = nv.lit {
remote = Some(lit.value());
} else {
return Err(Error::new_spanned(
&nv.lit,
"Attribute 'remote' should be a string.",
));
}
}
}
_ => {}
@ -368,6 +379,7 @@ impl Enum {
internal,
name,
desc,
remote,
})
}
}

View File

@ -1,5 +1,5 @@
use crate::args;
use crate::utils::{get_crate_name, get_rustdoc};
use crate::utils::{get_cfg_attrs, get_crate_name, get_rustdoc};
use inflector::Inflector;
use proc_macro::TokenStream;
use quote::quote;
@ -39,11 +39,6 @@ pub fn generate(enum_args: &args::Enum, input: &DeriveInput) -> Result<TokenStre
}
let item_ident = &variant.ident;
let item_attrs = variant
.attrs
.iter()
.filter(|attr| !attr.path.is_ident("item"))
.collect::<Vec<_>>();
let mut item_args = args::EnumItem::parse(&variant.attrs)?;
let gql_item_name = item_args
.name
@ -59,7 +54,7 @@ pub fn generate(enum_args: &args::Enum, input: &DeriveInput) -> Result<TokenStre
.as_ref()
.map(|s| quote! { Some(#s) })
.unwrap_or_else(|| quote! {None});
enum_items.push(quote! { #(#item_attrs)* #item_ident});
enum_items.push((get_cfg_attrs(&variant.attrs), item_ident));
items.push(quote! {
#crate_name::resolver_utils::EnumItem {
name: #gql_item_name,
@ -75,6 +70,49 @@ pub fn generate(enum_args: &args::Enum, input: &DeriveInput) -> Result<TokenStre
});
}
let remote_conversion = if let Some(remote) = &enum_args.remote {
let remote_ty = if let Ok(ty) = syn::parse_str::<syn::Type>(remote) {
ty
} else {
return Err(Error::new_spanned(
remote,
format!("Invalid remote type: '{}'", remote),
));
};
let local_to_remote_items = enum_items.iter().map(|(cfg_attrs, item)| {
quote! {
#(#cfg_attrs)*
#ident::#item => #remote_ty::#item,
}
});
let remote_to_local_items = enum_items.iter().map(|(cfg_attrs, item)| {
quote! {
#(#cfg_attrs)*
#remote_ty::#item => #ident::#item,
}
});
Some(quote! {
impl ::std::convert::From<#ident> for #remote_ty {
fn from(value: #ident) -> Self {
match value {
#(#local_to_remote_items)*
}
}
}
impl ::std::convert::From<#remote_ty> for #ident {
fn from(value: #remote_ty) -> Self {
match value {
#(#remote_to_local_items)*
}
}
}
})
} else {
None
};
let expanded = quote! {
#[allow(clippy::all, clippy::pedantic)]
impl #crate_name::resolver_utils::EnumType for #ident {
@ -122,6 +160,8 @@ pub fn generate(enum_args: &args::Enum, input: &DeriveInput) -> Result<TokenStre
}
}
#remote_conversion
impl #crate_name::type_mark::TypeMarkEnum for #ident {}
};
Ok(expanded.into())

View File

@ -70,3 +70,25 @@ pub async fn test_enum_derive_and_item_attributes() {
TestStruct { value: Test::Real }
);
}
#[async_std::test]
pub async fn test_remote_enum() {
#[derive(Enum, Copy, Clone, Eq, PartialEq)]
#[graphql(remote = "remote::RemoteEnum")]
enum LocalEnum {
A,
B,
C,
}
mod remote {
pub enum RemoteEnum {
A,
B,
C,
}
}
let _: remote::RemoteEnum = LocalEnum::A.into();
let _: LocalEnum = remote::RemoteEnum::A.into();
}