123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204 |
- use convert_case::{Case, Casing};
- use quote::{format_ident, quote};
- pub fn derive(tokens: proc_macro::TokenStream) -> proc_macro::TokenStream {
- let input: syn::DeriveInput = syn::parse_macro_input!(tokens);
- let parts = if let syn::Data::Struct(syn::DataStruct {
- struct_token: _,
- fields: syn::Fields::Named(fields),
- semi_token: _,
- }) = input.data
- {
- fields
- .named
- .into_iter()
- .map(|f| (f.ident.unwrap(), f.ty, f.attrs))
- .collect::<Vec<_>>()
- } else {
- panic!("Can only derive Entity on data structs with named fields!");
- };
- let entity_ident = input.ident;
- let make_combined_name = |part: &(syn::Ident, syn::Type, _)| {
- format_ident!(
- "{}{}PartType",
- entity_ident,
- part.0.to_string().to_case(Case::UpperCamel)
- )
- };
- let make_part_list = |plist: &Vec<_>| match plist.len() {
- 0 => quote! { () },
- 1 => {
- let ty = make_combined_name(&plist.first().as_ref().unwrap());
- quote! { #ty }
- }
- _ => {
- let tys = plist.iter().map(|part| make_combined_name(&part));
- quote! { ( #(#tys),* ) }
- }
- };
- let vis = input.vis;
- let unique_ident = format_ident!("unique");
- // collect list of unique parts
- let unique_parts = parts
- .iter()
- .filter(|part| {
- part.2.iter().any(|attr| {
- attr.parse_meta()
- .map(|a| a.path().is_ident(&unique_ident))
- .unwrap_or(false)
- })
- })
- .cloned()
- .collect::<Vec<_>>();
- let part_defs = parts.iter().map(|part| {
- let part_combined_name = make_combined_name(&part);
- let part_base_name = &part.0.to_string();
- let part_type = &part.1;
- let unique = unique_parts.iter().any(|p| p.0 == part.0);
- quote! {
- #vis struct #part_combined_name;
- impl ::microrm::entity::EntityPart for #part_combined_name {
- type Datum = #part_type;
- type Entity = #entity_ident;
- fn part_name() -> &'static str {
- #part_base_name
- }
- fn unique() -> bool {
- #unique
- }
- }
- }
- });
- let part_visit = parts.iter().map(|part| {
- let part_combined_name = make_combined_name(&part);
- quote! {
- v.visit::<#part_combined_name>();
- }
- });
- let part_ref_visit = parts.iter().map(|part| {
- let part_combined_name = make_combined_name(&part);
- let field = &part.0;
- quote! {
- v.visit_datum::<#part_combined_name>(&self.#field);
- }
- });
- let part_names = parts.iter().map(|part| {
- let part_combined_name = make_combined_name(&part);
- let part_camel_name = format_ident!("{}", part.0.to_string().to_case(Case::UpperCamel));
- quote! {
- pub const #part_camel_name : #part_combined_name = #part_combined_name;
- }
- });
- let build_struct = parts
- .iter()
- .enumerate()
- .map(|(i, part)| {
- let ident = &part.0;
- match parts.len() {
- 1 => {
- quote! {
- #ident: values
- }
- }
- _ => {
- let idx = syn::Index::from(i);
- quote! {
- #ident: values. #idx
- }
- }
- }
- })
- .collect::<Vec<_>>();
- let parts_list = make_part_list(&parts);
- let uniques_list = make_part_list(&unique_parts);
- let entity_name = entity_ident.to_string().to_case(Case::Snake);
- let id_ident = format_ident!("{}ID", entity_ident);
- quote! {
- #(#part_defs)*
- impl #entity_ident {
- #(#part_names)*
- }
- #[derive(Clone, Copy, PartialEq, PartialOrd, Debug, Hash)]
- #vis struct #id_ident (i64);
- impl ::microrm::entity::EntityID for #id_ident {
- type Entity = #entity_ident;
- fn from_raw(raw: i64) -> Self { Self(raw) }
- fn into_raw(self) -> i64 { self.0 }
- }
- impl ::microrm::datum::Datum for #id_ident {
- fn sql_type() -> &'static str {
- <i64 as ::microrm::datum::Datum>::sql_type()
- }
- fn bind_to<'a>(&self, stmt: &mut ::microrm::sqlite::Statement<'a>, index: usize) {
- <i64 as ::microrm::datum::Datum>::bind_to(&self.0, stmt, index)
- }
- fn build_from<'a>(
- adata: ::microrm::schema::AssocData,
- stmt: &mut ::microrm::sqlite::Statement<'a>,
- index: usize,
- ) -> ::microrm::DBResult<(Self, usize)>
- where
- Self: Sized,
- {
- let raw = <i64 as ::microrm::datum::Datum>::build_from(adata, stmt, index)?;
- Ok((Self(raw.0), raw.1))
- }
- fn accept_discriminator(d: &mut impl ::microrm::schema::DatumDiscriminator) where Self: Sized {
- d.visit_entity_id::<#entity_ident>();
- }
- }
- impl ::microrm::entity::Entity for #entity_ident {
- type Parts = #parts_list;
- type Uniques = #uniques_list;
- type ID = #id_ident;
- fn build(values: <Self::Parts as ::microrm::entity::EntityPartList>::DatumList) -> Self {
- Self {
- #(#build_struct),*
- }
- }
- fn entity_name() -> &'static str { #entity_name }
- fn accept_part_visitor(v: &mut impl ::microrm::entity::EntityPartVisitor) {
- #(
- #part_visit
- );*
- }
- fn accept_part_visitor_ref(&self, v: &mut impl ::microrm::entity::EntityPartVisitor) {
- #(
- #part_ref_visit
- );*
- }
- }
- }
- .into()
- }
|