diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..2940dc7 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "derive-sample" +version = "0.1.0" +authors = ["Hatter Jiang "] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[lib] +proc-macro = true + +[dependencies] +proc-macro2 = "1.0" +quote = "1.0" +syn = "1.0" diff --git a/README.md b/README.md index 102551c..551363f 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,6 @@ # derive-sample -Rust derive sample \ No newline at end of file +Rust derive sample + + +Modified from: https://github.com/dtolnay/serde-repr diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..34e595e --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,48 @@ +extern crate proc_macro; + +// #![recursion_limit = "128"] + +mod parse; + +use proc_macro::TokenStream; +use quote::quote; +use syn::parse_macro_input; + +use parse::Input; + +// #[proc_macro_attribute] +// pub fn dsample(attr: TokenStream, item: TokenStream) -> TokenStream { +// println!("attr: \"{}\"", attr.to_string()); +// println!("item: \"{}\"", item.to_string()); +// item +// } + +#[proc_macro_derive(Serialize_repr, attributes(dsample))] +pub fn derive_serialize(input: TokenStream) -> TokenStream { + let input = parse_macro_input!(input as Input); + println!(""); + println!("::::: {:?}", &input); + let ident = input.ident; + let repr = input.repr; + + let match_variants = input.variants.iter().map(|variant| { + let variant = &variant.ident; + quote! { + #ident::#variant => #ident::#variant as #repr, + } + }); + + TokenStream::from(quote! { + // println!("{}", #ident); + // impl serde::Serialize for #ident { + // fn serialize(&self, serializer: S) -> core::result::Result + // where S: serde::Serializer + // { + // let value: #repr = match *self { + // #(#match_variants)* + // }; + // serde::Serialize::serialize(&value, serializer) + // } + // } + }) +} diff --git a/src/parse.rs b/src/parse.rs new file mode 100644 index 0000000..0954413 --- /dev/null +++ b/src/parse.rs @@ -0,0 +1,134 @@ +use proc_macro2::Span; +use syn::parse::{Error, Parse, ParseStream, Parser, Result}; +use syn::{parenthesized, Data, DeriveInput, Fields, Ident, Meta, NestedMeta}; + +#[derive(Debug)] +pub struct Input { + pub ident: Ident, // enum ident + pub repr: Ident, + pub variants: Vec, + pub default_variant: Option, +} + +#[derive(Clone, Debug)] +pub struct Variant { + pub ident: Ident, + pub attrs: VariantAttrs, +} + +#[derive(Clone, Debug)] +pub struct VariantAttrs { + pub is_default: bool, +} + +fn parse_meta(attrs: &mut VariantAttrs, meta: &Meta) { + if let Meta::List(value) = meta { + for meta in &value.nested { + if let NestedMeta::Meta(Meta::Path(path)) = meta { + if path.is_ident("other") { + attrs.is_default = true; + } + } + } + } +} + +fn parse_attrs(variant: &syn::Variant) -> Result { + let mut attrs = VariantAttrs { is_default: false }; + println!("---- {:?}", &variant.ident); + for attr in &variant.attrs { + // #[serde(other, rename = "useless")] + println!("==== {:?}", attr.path.is_ident("dsample")); + if attr.path.is_ident("dsample") { + let meta = attr.parse_meta()?; + if let Meta::List(value) = meta { + for meta in &value.nested { + if let NestedMeta::Meta(Meta::Path(path)) = meta { + // if path.is_ident("other") { + // attrs.is_default = true; + // } + // println!(".......{:?}", path); + } + } + } + } + if attr.path.is_ident("serde") { + parse_meta(&mut attrs, &attr.parse_meta()?); + } + } + Ok(attrs) +} + +impl Parse for Input { + fn parse(input: ParseStream) -> Result { + let call_site = Span::call_site(); + let derive_input = DeriveInput::parse(input)?; + + let data = match derive_input.data { + Data::Enum(data) => data, + _ => { + return Err(Error::new(call_site, "input must be an enum")); + } + }; + + let variants = data + .variants + .into_iter() + .map(|variant| match variant.fields { + Fields::Unit => { + let attrs = parse_attrs(&variant)?; + Ok(Variant { + ident: variant.ident, + attrs, + }) + } + Fields::Named(_) | Fields::Unnamed(_) => { + Err(Error::new(variant.ident.span(), "must be a unit variant")) + } + }) + .collect::>>()?; + + if variants.is_empty() { + return Err(Error::new(call_site, "there must be at least one variant")); + } + + let generics = derive_input.generics; + if !generics.params.is_empty() || generics.where_clause.is_some() { + return Err(Error::new(call_site, "generic enum is not supported")); + } + + println!(">>>>>>>>>>>>>>>>>>"); + + let mut repr = None; + for attr in derive_input.attrs { + println!(">>>>>>> {:?}", &attr.tokens); + if attr.path.is_ident("repr") { + fn repr_arg(input: ParseStream) -> Result { + let content; + parenthesized!(content in input); + content.parse() + } + let ty = repr_arg.parse2(attr.tokens)?; + repr = Some(ty); + break; + } + } + let repr = repr.ok_or_else(|| Error::new(call_site, "missing #[repr(...)] attribute"))?; + + let mut default_variants = variants.iter().filter(|x| x.attrs.is_default); + let default_variant = default_variants.next().cloned(); + if default_variants.next().is_some() { + return Err(Error::new( + call_site, + "only one variant can be #[serde(other)]", + )); + } + + Ok(Input { + ident: derive_input.ident, + repr, + variants, + default_variant, + }) + } +} \ No newline at end of file diff --git a/tests/test.rs b/tests/test.rs new file mode 100644 index 0000000..ee75f8f --- /dev/null +++ b/tests/test.rs @@ -0,0 +1,27 @@ +use derive_sample::{Serialize_repr}; +// use derive_sample::dsample; + +mod small_prime { + use super::*; + + // #[dsample(name = "X")] + #[derive(Serialize_repr)] + #[repr(u8)] + #[dsample(a= "b")] + enum SmallPrime { + Two = 2, + Three = 3, + Five = 5, + #[dsample] + #[dsample(name = "aaaa")] + Seven = 7, + } + + + #[test] + fn test_serialize() { + println!("hello world"); + // let j = serde_json::to_string(&SmallPrime::Seven).unwrap(); + // assert_eq!(j, "7"); + } +} \ No newline at end of file