use proc_macro::TokenStream; use syn::{parse_macro_input, DeriveInput}; use quote::{quote,format_ident}; use convert_case::{Case, Casing}; #[proc_macro_derive(Entity, attributes(microrm))] pub fn derive_model(tokens: TokenStream) -> TokenStream { let input = parse_macro_input!(tokens as DeriveInput); let struct_name = &input.ident; let enum_name = format_ident!("{}Columns", &input.ident); let table_name = format!("{}", struct_name).to_case(Case::Snake); if let syn::Data::Struct(st) = input.data { if let syn::Fields::Named(fields) = st.fields { let mut variants = syn::punctuated::Punctuated::::new(); let mut field_names = syn::punctuated::Punctuated::::new(); let mut value_references = syn::punctuated::Punctuated::::new(); for name in fields.named.iter() { let converted_case = format!("{}", name.ident.as_ref().unwrap().clone()).to_case(Case::UpperCamel); let converted_case = format_ident!("{}", converted_case); variants.push(converted_case.clone()); let field_name = name.ident.as_ref().unwrap().clone(); let field_name_str = format!("{}", field_name); field_names.push(quote!{ Self::Column::#converted_case => #field_name_str }.into()); value_references.push(quote!{ &self. #field_name }); } let ret = quote!{ #[derive(Clone,Copy,strum::IntoStaticStr,strum::EnumCount)] enum #enum_name { #variants } impl crate::model::Entity for #struct_name { fn table_name() -> &'static str { #table_name } type Column = #enum_name; fn column_count() -> usize { ::COUNT } fn index(c: Self::Column) -> usize { c as usize } fn name(c: Self::Column) -> &'static str { match c { #field_names } } fn values(&self) -> Vec<&dyn rusqlite::ToSql> { vec![ #value_references ] } } }.into(); ret } else { panic!("Can only use derive(Model) on non-unit structs with named fields!") } } else { panic!("Can only use derive(Model) on structs!") } }