|
@@ -19,6 +19,16 @@ fn parse_microrm_ref(attrs: &[syn::Attribute]) -> proc_macro2::TokenStream {
|
|
|
quote! { ::microrm }
|
|
|
}
|
|
|
|
|
|
+fn parse_fk(attrs: &[syn::Attribute]) -> bool {
|
|
|
+ for attr in attrs {
|
|
|
+ if attr.path.segments.len() == 1 && attr.path.segments.last().unwrap().ident == "microrm_foreign" {
|
|
|
+ return true
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ false
|
|
|
+}
|
|
|
+
|
|
|
/// Turns a serializable/deserializable struct into a microrm entity model.
|
|
|
///
|
|
|
/// There are two important visible effects:
|
|
@@ -34,7 +44,7 @@ fn parse_microrm_ref(attrs: &[syn::Attribute]) -> proc_macro2::TokenStream {
|
|
|
/// - `#[microrm_internal]`: this is internal to the microrm crate (of extremely limited usefulness
|
|
|
/// outside the microrm library)
|
|
|
/// The following are understood on individual fields
|
|
|
-/// - `#[microrm_foreign]`: this is a foreign key (and must be an type implementing `EntityID`)
|
|
|
+/// - `#[microrm_foreign]`: this is a foreign key (and the field must be of a type implementing `EntityID`)
|
|
|
#[proc_macro_derive(Entity, attributes(microrm_internal, microrm_foreign))]
|
|
|
pub fn derive_entity(tokens: TokenStream) -> TokenStream {
|
|
|
let input = parse_macro_input!(tokens as DeriveInput);
|
|
@@ -56,11 +66,13 @@ pub fn derive_entity(tokens: TokenStream) -> TokenStream {
|
|
|
_ => panic!("Can only use derive(Entity) on non-unit structs with named fields!"),
|
|
|
};
|
|
|
|
|
|
- let mut variants = syn::punctuated::Punctuated::<syn::Ident, syn::token::Comma>::new();
|
|
|
- let mut field_names =
|
|
|
- syn::punctuated::Punctuated::<proc_macro2::TokenStream, syn::token::Comma>::new();
|
|
|
- let mut value_references =
|
|
|
- syn::punctuated::Punctuated::<proc_macro2::TokenStream, syn::token::Comma>::new();
|
|
|
+ let mut variants = Vec::new();
|
|
|
+ let mut field_names = Vec::new();
|
|
|
+ let mut value_references = Vec::new();
|
|
|
+
|
|
|
+ let mut foreign_keys = Vec::new();
|
|
|
+ let mut foreign_key_impls = Vec::new();
|
|
|
+
|
|
|
for name in fields.named.iter() {
|
|
|
let converted_case =
|
|
|
format!("{}", name.ident.as_ref().unwrap().clone()).to_case(Case::UpperCamel);
|
|
@@ -71,9 +83,34 @@ pub fn derive_entity(tokens: TokenStream) -> TokenStream {
|
|
|
let field_name_str = format!("{}", field_name);
|
|
|
field_names.push(quote! { Self::Column::#converted_case => #field_name_str });
|
|
|
|
|
|
+ if parse_fk(&name.attrs) {
|
|
|
+ let fk_struct_name = format_ident!("{}{}ForeignKey", struct_name, converted_case);
|
|
|
+ let ty = &name.ty;
|
|
|
+ foreign_keys.push(quote!{
|
|
|
+ &#fk_struct_name { col: #enum_name::#converted_case }
|
|
|
+ });
|
|
|
+ foreign_key_impls.push(quote!{
|
|
|
+ struct #fk_struct_name {
|
|
|
+ col: #enum_name
|
|
|
+ }
|
|
|
+ impl #microrm_ref::model::EntityForeignKey<#enum_name> for #fk_struct_name {
|
|
|
+ fn local_column(&self) -> &#enum_name { &self.col }
|
|
|
+ fn foreign_table_name(&self) -> &'static str {
|
|
|
+ <<#ty as #microrm_ref::model::EntityID>::Entity as #microrm_ref::model::Entity>::table_name()
|
|
|
+ }
|
|
|
+ fn foreign_column_name(&self) -> &'static str {
|
|
|
+ "id"
|
|
|
+ }
|
|
|
+ }
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
value_references.push(quote! { &self. #field_name });
|
|
|
}
|
|
|
|
|
|
+ let fk_array_ident = format_ident!("FOREIGN_KEYS_{}", struct_name);
|
|
|
+ let fk_array_len = foreign_keys.len();
|
|
|
+
|
|
|
let field_count = fields.named.iter().count();
|
|
|
|
|
|
quote!{
|
|
@@ -82,7 +119,7 @@ pub fn derive_entity(tokens: TokenStream) -> TokenStream {
|
|
|
#[allow(unused)]
|
|
|
pub enum #enum_name {
|
|
|
ID,
|
|
|
- #variants
|
|
|
+ #(#variants),*
|
|
|
}
|
|
|
|
|
|
#[derive(Debug,PartialEq,Clone,Copy,#microrm_ref::re_export::serde::Serialize,#microrm_ref::re_export::serde::Deserialize)]
|
|
@@ -101,7 +138,7 @@ pub fn derive_entity(tokens: TokenStream) -> TokenStream {
|
|
|
}
|
|
|
|
|
|
impl #microrm_ref::re_export::rusqlite::ToSql for #id_name {
|
|
|
- fn to_sql(&self) -> Result<#microrm_ref::re_export::rusqlite::types::ToSqlOutput<'_>, #microrm_ref::re_export::rusqlite::Error> {
|
|
|
+ fn to_sql(&self) -> #microrm_ref::re_export::rusqlite::Result<#microrm_ref::re_export::rusqlite::types::ToSqlOutput<'_>> {
|
|
|
self.0.to_sql()
|
|
|
}
|
|
|
}
|
|
@@ -122,13 +159,19 @@ pub fn derive_entity(tokens: TokenStream) -> TokenStream {
|
|
|
fn name(c: Self::Column) -> &'static str {
|
|
|
match c {
|
|
|
Self::Column::ID => "ID",
|
|
|
- #field_names
|
|
|
+ #(#field_names),*
|
|
|
}
|
|
|
}
|
|
|
fn values(&self) -> Vec<&dyn #microrm_ref::re_export::rusqlite::ToSql> {
|
|
|
- vec![ #value_references ]
|
|
|
+ vec![ #(#value_references),* ]
|
|
|
+ }
|
|
|
+
|
|
|
+ fn foreign_keys() -> &'static [&'static dyn #microrm_ref::model::EntityForeignKey<Self::Column>] {
|
|
|
+ &[#(#foreign_keys),*]
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
+ #(#foreign_key_impls)*
|
|
|
}.into()
|
|
|
}
|
|
|
|