//! not much - to be removed use darling::{FromDeriveInput, FromField}; use quote::{quote, ToTokens}; use syn::{parse_str, Expr}; #[derive(Clone, Debug, FromField)] #[darling(forward_attrs(doc))] struct StructField { ident: Option, ty: syn::Type, // vis: syn::Visibility, attrs: Vec, } #[derive(Debug, FromDeriveInput)] #[darling(supports(struct_named))] struct Cat2 { ident: syn::Ident, data: darling::ast::Data<(), StructField>, generics: syn::Generics, } impl ToTokens for Cat2 { fn to_tokens(&self, out: &mut proc_macro2::TokenStream) { let Cat2 { ref ident, ref generics, ref data, } = *self; // let tokens = quote! {}; dbg!(&ident); let (imp, ty, wher) = generics.split_for_impl(); let fields = data.as_ref().take_struct().unwrap().fields; let fields = fields .into_iter() .map(|field| { let StructField { ref ident, ref ty, // ref vis, ref attrs, .. } = *field; // let ident = ident.unwrap(); let doc = attrs .iter() .filter(|attr| attr.path().is_ident("doc")) .filter_map(|attr| match &attr.meta { syn::Meta::NameValue(syn::MetaNameValue { value: Expr::Lit(syn::ExprLit { lit: syn::Lit::Str(s), .. }), .. }) => Some(s.value()), _ => None, }) .fold(String::new(), |mut acc, s| { if !acc.is_empty() { acc.push(' '); } acc.push_str(s.trim()); acc }); let doc = if doc.is_empty() { "undocumented".to_string() } else { doc }; // dbg!(&doc); dbg!(ty); // (ident, format!("<{}> {}", #ty, #doc)) quote! { (stringify!(#ident).to_string(), "<" + stringigy!(#ty) + ">" + #doc).into() } }) .collect::>(); // dbg!(fields); out.extend(quote! { impl #imp Cat2 for #ident #ty #wher { fn describe(&self) -> Vec { vec![ #(#fields),* ].into() } } }) } } fn main() { let input = r#"#[derive(Cat2)] pub struct Foo { /// Hello I'm bar /// And I'm a multiline doc #[my_trait(volume = "whisper")] bar: bool, /// Hello I'm baz baz: i64, foo: Vec, }"#; let parsed = parse_str(input).unwrap(); let receiver = Cat2::from_derive_input(&parsed).unwrap(); let tokens = quote!(#receiver); println!( r#" INPUT: {} PARSED AS: {:?} EMITS: {} "#, input, receiver, tokens ); }