|
@@ -16,85 +16,183 @@ fn parse_fk(attrs: &[syn::Attribute]) -> bool {
|
|
|
false
|
|
|
}
|
|
|
|
|
|
+fn derive_columns<'a, I: Iterator<Item=&'a syn::Field>>(input: &DeriveInput, microrm_ref: &proc_macro2::TokenStream, fields: I) -> proc_macro2::TokenStream {
|
|
|
+ let struct_name = &input.ident;
|
|
|
+ let columns_name = format_ident!("_{}_columns", &input.ident.to_string().to_case(Case::Snake));
|
|
|
+ let mut index = 0usize;
|
|
|
+
|
|
|
+ let mut column_types = Vec::new();
|
|
|
+ let mut column_impls = Vec::new();
|
|
|
+ let mut column_consts = Vec::new();
|
|
|
+ let mut column_array = Vec::new();
|
|
|
+
|
|
|
+ for name in fields {
|
|
|
+ let original_case = name.ident.as_ref().unwrap().clone();
|
|
|
+ let snake_case = original_case.to_string();
|
|
|
+ if snake_case != snake_case.to_case(Case::Snake) {
|
|
|
+ return quote::quote_spanned!(original_case.span() => compile_error!("Names must be in snake_case")).into()
|
|
|
+ }
|
|
|
+
|
|
|
+ let converted_case = format_ident!("{}", original_case.to_string().to_case(Case::UpperCamel));
|
|
|
+
|
|
|
+ let ty = &name.ty;
|
|
|
+
|
|
|
+ index += 1;
|
|
|
+ column_types.push(quote! { pub struct #converted_case (); });
|
|
|
+ column_impls.push(quote! {
|
|
|
+ impl #microrm_ref::entity::EntityColumn for #columns_name::#converted_case {
|
|
|
+ type Entity = super::#struct_name;
|
|
|
+ // XXX: #ty here may be in parent context
|
|
|
+ fn sql_type(&self) -> &'static str { <#ty as #microrm_ref::model::Modelable>::column_type() }
|
|
|
+ fn index(&self) -> usize { #index }
|
|
|
+ fn name(&self) -> &'static str { #snake_case }
|
|
|
+ }
|
|
|
+ });
|
|
|
+ column_consts.push(quote! { const #converted_case : #columns_name::#converted_case = #columns_name::#converted_case(); });
|
|
|
+
|
|
|
+ column_array.push(quote! { & #columns_name::#converted_case() });
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ let columns_array_name = format_ident!(
|
|
|
+ "{}_COLUMNS",
|
|
|
+ struct_name.to_string().to_case(Case::ScreamingSnake)
|
|
|
+ );
|
|
|
+
|
|
|
+ quote!{
|
|
|
+ pub mod #columns_name {
|
|
|
+ pub struct ID ();
|
|
|
+ #(#column_types)*
|
|
|
+ }
|
|
|
+
|
|
|
+ impl #microrm_ref::entity::EntityColumn for #columns_name::ID {
|
|
|
+ type Entity = #struct_name;
|
|
|
+ fn sql_type(&self) -> &'static str { "integer" }
|
|
|
+ fn index(&self) -> usize { 0 }
|
|
|
+ fn name(&self) -> &'static str { "id" }
|
|
|
+ }
|
|
|
+ #(#column_impls)*
|
|
|
+
|
|
|
+ impl #struct_name {
|
|
|
+ #(#column_consts)*
|
|
|
+ }
|
|
|
+
|
|
|
+ #microrm_ref::re_export::lazy_static::lazy_static!{
|
|
|
+ static ref #columns_array_name: [&'static dyn #microrm_ref::entity::EntityColumn<Entity = #struct_name>;#index + 1] = {
|
|
|
+ [ &#columns_name::ID(), #(#column_array),* ]
|
|
|
+ };
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+fn derive_id(input: &DeriveInput, microrm_ref: &proc_macro2::TokenStream) -> proc_macro2::TokenStream {
|
|
|
+ let struct_name = &input.ident;
|
|
|
+ let id_name = format_ident!("{}ID", &input.ident);
|
|
|
+
|
|
|
+ quote! {
|
|
|
+ #[derive(Debug,PartialEq,Clone,Copy,#microrm_ref::re_export::serde::Serialize,#microrm_ref::re_export::serde::Deserialize)]
|
|
|
+ #[allow(unused)]
|
|
|
+ pub struct #id_name (i64);
|
|
|
+
|
|
|
+ impl #microrm_ref::entity::EntityID for #id_name {
|
|
|
+ type Entity = #struct_name;
|
|
|
+ fn from_raw_id(raw: i64) -> Self { Self(raw) }
|
|
|
+ fn raw_id(&self) -> i64 { self.0 }
|
|
|
+ }
|
|
|
+
|
|
|
+ impl #microrm_ref::model::Modelable for #id_name {
|
|
|
+ fn bind_to(&self, stmt: &mut #microrm_ref::re_export::sqlite::Statement, col: usize) -> #microrm_ref::re_export::sqlite::Result<()> {
|
|
|
+ use #microrm_ref::re_export::sqlite::Bindable;
|
|
|
+ self.0.bind(stmt, col)
|
|
|
+ }
|
|
|
+ fn build_from(stmt: &#microrm_ref::re_export::sqlite::Statement, col_offset: usize) -> #microrm_ref::re_export::sqlite::Result<(Self, usize)> where Self: Sized {
|
|
|
+ stmt.read::<i64>(col_offset).map(|x| (#id_name(x), 1))
|
|
|
+ }
|
|
|
+ fn column_type() -> &'static str where Self: Sized {
|
|
|
+ "integer"
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }.into()
|
|
|
+}
|
|
|
+
|
|
|
pub(crate) fn derive(tokens: TokenStream) -> TokenStream {
|
|
|
let input = parse_macro_input!(tokens as DeriveInput);
|
|
|
|
|
|
let microrm_ref = crate::parse_microrm_ref(&input.attrs);
|
|
|
|
|
|
let struct_name = &input.ident;
|
|
|
- let enum_name = format_ident!("{}Columns", &input.ident);
|
|
|
let id_name = format_ident!("{}ID", &input.ident);
|
|
|
|
|
|
let table_name = format!("{}", struct_name).to_case(Case::Snake);
|
|
|
|
|
|
- let st = match input.data {
|
|
|
+ let st = match &input.data {
|
|
|
syn::Data::Struct(st) => st,
|
|
|
_ => panic!("Can only use derive(Entity) on structs!"),
|
|
|
};
|
|
|
- let fields = match st.fields {
|
|
|
+ let fields = match &st.fields {
|
|
|
syn::Fields::Named(fields) => fields,
|
|
|
_ => panic!("Can only use derive(Entity) on non-unit structs with named fields!"),
|
|
|
};
|
|
|
|
|
|
let mut variants = Vec::new();
|
|
|
- let mut field_names = Vec::new();
|
|
|
- let mut field_numbers = Vec::new();
|
|
|
- let mut field_types = Vec::new();
|
|
|
let mut value_references = Vec::new();
|
|
|
|
|
|
- let mut foreign_keys = Vec::new();
|
|
|
- let mut foreign_key_impls = Vec::new();
|
|
|
-
|
|
|
let mut build_clauses = Vec::new();
|
|
|
|
|
|
let mut index: usize = 0;
|
|
|
|
|
|
+ let column_output = derive_columns(&input, µrm_ref, fields.named.iter());
|
|
|
+ let id_output = derive_id(&input, µrm_ref);
|
|
|
+
|
|
|
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);
|
|
|
+ let original_case = name.ident.as_ref().unwrap().clone();
|
|
|
+ let snake_case = original_case.to_string().to_case(Case::Snake);
|
|
|
+
|
|
|
+ if original_case.to_string() != snake_case {
|
|
|
+ return quote::quote_spanned!(original_case.span() => compile_error!("Names must be in snake_case")).into()
|
|
|
+ }
|
|
|
+
|
|
|
+ let converted_case = format_ident!("{}", original_case.to_string().to_case(Case::UpperCamel));
|
|
|
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 });
|
|
|
-
|
|
|
- let nn = field_numbers.len() + 1;
|
|
|
- field_numbers.push(quote! { #nn => Self::#converted_case, });
|
|
|
+ value_references.push(quote! { &self. #field_name });
|
|
|
|
|
|
let ty = &name.ty;
|
|
|
- field_types.push(quote! { <#ty as #microrm_ref::model::Modelable>::column_type() });
|
|
|
|
|
|
+ index += 1;
|
|
|
+ build_clauses.push(quote! { #field_name: <#ty as #microrm_ref::model::Modelable>::build_from(stmt, #index)?.0 });
|
|
|
+
|
|
|
+ /*
|
|
|
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 }
|
|
|
+ &#fk_struct_name { col: #columns_name::#converted_case }
|
|
|
});
|
|
|
foreign_key_impls.push(quote!{
|
|
|
struct #fk_struct_name {
|
|
|
- col: #enum_name
|
|
|
+ col: #columns_name
|
|
|
}
|
|
|
- impl #microrm_ref::entity::EntityForeignKey<#enum_name> for #fk_struct_name {
|
|
|
- fn local_column(&self) -> &#enum_name { &self.col }
|
|
|
+ impl #microrm_ref::entity::EntityForeignKey<#columns_name> for #fk_struct_name {
|
|
|
+ /*
|
|
|
+ fn local_column(&self) -> &#columns_name { &self.col }
|
|
|
fn foreign_table_name(&self) -> &'static str {
|
|
|
<<#ty as #microrm_ref::entity::EntityID>::Entity as #microrm_ref::entity::Entity>::table_name()
|
|
|
}
|
|
|
fn foreign_column_name(&self) -> &'static str {
|
|
|
"id"
|
|
|
}
|
|
|
+ */
|
|
|
}
|
|
|
});
|
|
|
}
|
|
|
+ */
|
|
|
|
|
|
- value_references.push(quote! { &self. #field_name });
|
|
|
-
|
|
|
- index += 1;
|
|
|
- build_clauses.push(quote! { #field_name: <#ty as #microrm_ref::model::Modelable>::build_from(stmt, #index)?.0 });
|
|
|
}
|
|
|
|
|
|
- let column_types_name = format_ident!(
|
|
|
- "{}_COLUMN_TYPES",
|
|
|
+ let columns_array_name = format_ident!(
|
|
|
+ "{}_COLUMNS",
|
|
|
struct_name.to_string().to_case(Case::ScreamingSnake)
|
|
|
);
|
|
|
|
|
@@ -102,65 +200,12 @@ pub(crate) fn derive(tokens: TokenStream) -> TokenStream {
|
|
|
|
|
|
quote!{
|
|
|
// Related types for #struct_name
|
|
|
- #[derive(Clone,Copy,PartialEq,Hash)]
|
|
|
- #[allow(unused)]
|
|
|
- #[repr(usize)]
|
|
|
- pub enum #enum_name {
|
|
|
- ID,
|
|
|
- #(#variants),*
|
|
|
- }
|
|
|
-
|
|
|
- #[derive(Debug,PartialEq,Clone,Copy,#microrm_ref::re_export::serde::Serialize,#microrm_ref::re_export::serde::Deserialize)]
|
|
|
- #[allow(unused)]
|
|
|
- pub struct #id_name (i64);
|
|
|
-
|
|
|
- // Implementations for related types
|
|
|
- impl #microrm_ref::entity::EntityColumns for #enum_name {
|
|
|
- type Entity = #struct_name;
|
|
|
- }
|
|
|
|
|
|
- impl std::convert::From<usize> for #enum_name {
|
|
|
- fn from(i: usize) -> Self {
|
|
|
- match i {
|
|
|
- 0 => Self::ID,
|
|
|
- #(#field_numbers)*
|
|
|
- _ => {
|
|
|
- panic!("Given invalid usize to convert to column")
|
|
|
- },
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- impl #microrm_ref::entity::EntityID for #id_name {
|
|
|
- type Entity = #struct_name;
|
|
|
- fn from_raw_id(raw: i64) -> Self { Self(raw) }
|
|
|
- fn raw_id(&self) -> i64 { self.0 }
|
|
|
- }
|
|
|
-
|
|
|
- impl #microrm_ref::model::Modelable for #id_name {
|
|
|
- fn bind_to(&self, stmt: &mut #microrm_ref::re_export::sqlite::Statement, col: usize) -> #microrm_ref::re_export::sqlite::Result<()> {
|
|
|
- use #microrm_ref::re_export::sqlite::Bindable;
|
|
|
- self.0.bind(stmt, col)
|
|
|
- }
|
|
|
- fn build_from(stmt: &#microrm_ref::re_export::sqlite::Statement, col_offset: usize) -> #microrm_ref::re_export::sqlite::Result<(Self, usize)> where Self: Sized {
|
|
|
- stmt.read::<i64>(col_offset).map(|x| (#id_name(x), 1))
|
|
|
- }
|
|
|
- fn column_type() -> &'static str where Self: Sized {
|
|
|
- "integer"
|
|
|
- }
|
|
|
- }
|
|
|
- #microrm_ref::re_export::lazy_static::lazy_static!{
|
|
|
- static ref #column_types_name: [&'static str;#field_count + 1] = {
|
|
|
- [
|
|
|
- "id",
|
|
|
- #(#field_types),*
|
|
|
- ]
|
|
|
- };
|
|
|
- }
|
|
|
+ #column_output
|
|
|
+ #id_output
|
|
|
|
|
|
// Implementations for #struct_name
|
|
|
impl #microrm_ref::entity::Entity for #struct_name {
|
|
|
- type Column = #enum_name;
|
|
|
type ID = #id_name;
|
|
|
|
|
|
fn table_name() -> &'static str { #table_name }
|
|
@@ -168,15 +213,6 @@ pub(crate) fn derive(tokens: TokenStream) -> TokenStream {
|
|
|
// +1 for ID column
|
|
|
#field_count + 1
|
|
|
}
|
|
|
- fn index(c: Self::Column) -> usize {
|
|
|
- c as usize
|
|
|
- }
|
|
|
- fn name(c: Self::Column) -> &'static str {
|
|
|
- match c {
|
|
|
- Self::Column::ID => "ID",
|
|
|
- #(#field_names),*
|
|
|
- }
|
|
|
- }
|
|
|
fn values(&self) -> Vec<&dyn #microrm_ref::model::Modelable> {
|
|
|
vec![ #(#value_references),* ]
|
|
|
}
|
|
@@ -187,15 +223,16 @@ pub(crate) fn derive(tokens: TokenStream) -> TokenStream {
|
|
|
})
|
|
|
}
|
|
|
|
|
|
- fn column_types() -> &'static [&'static str] {
|
|
|
- #column_types_name.as_ref()
|
|
|
+ fn columns() -> &'static [&'static dyn #microrm_ref::entity::EntityColumn<Entity = Self>] {
|
|
|
+ #columns_array_name.as_ref()
|
|
|
}
|
|
|
- fn foreign_keys() -> &'static [&'static dyn #microrm_ref::entity::EntityForeignKey<Self::Column>] {
|
|
|
+
|
|
|
+ /*fn foreign_keys() -> &'static [&'static dyn #microrm_ref::entity::EntityForeignKey<Self::Column>] {
|
|
|
&[#(#foreign_keys),*]
|
|
|
- }
|
|
|
+ }*/
|
|
|
}
|
|
|
|
|
|
// Foreign key struct implementations
|
|
|
- #(#foreign_key_impls)*
|
|
|
+ // #(#foreign_key_impls)*
|
|
|
}.into()
|
|
|
}
|