entity.rs 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274
  1. use proc_macro::TokenStream;
  2. use quote::{format_ident, quote};
  3. use syn::{parse_macro_input, DeriveInput};
  4. use convert_case::{Case, Casing};
  5. fn parse_fk(attrs: &[syn::Attribute]) -> bool {
  6. for attr in attrs {
  7. if attr.path.segments.len() == 1
  8. && attr.path.segments.last().unwrap().ident == "microrm_foreign"
  9. {
  10. return true;
  11. }
  12. }
  13. false
  14. }
  15. fn derive_columns<'a, I: Iterator<Item = &'a syn::Field>>(
  16. input: &DeriveInput,
  17. microrm_ref: &proc_macro2::TokenStream,
  18. fields: I,
  19. ) -> proc_macro2::TokenStream {
  20. let struct_name = &input.ident;
  21. let columns_name = format_ident!("_{}_columns", &input.ident.to_string().to_case(Case::Snake));
  22. let mut index = 0usize;
  23. let mut column_types = Vec::new();
  24. let mut column_impls = Vec::new();
  25. let mut column_consts = Vec::new();
  26. let mut column_array = Vec::new();
  27. for name in fields {
  28. let original_case = name.ident.as_ref().unwrap().clone();
  29. let snake_case = original_case.to_string();
  30. if snake_case != snake_case.to_case(Case::Snake) {
  31. return quote::quote_spanned!(original_case.span() => compile_error!("Names must be in snake_case"));
  32. }
  33. let converted_case =
  34. format_ident!("{}", original_case.to_string().to_case(Case::UpperCamel));
  35. let ty = &name.ty;
  36. let mut fk_table_name = quote! { None };
  37. let mut fk_column_name = quote! { None };
  38. if parse_fk(&name.attrs) {
  39. fk_table_name = quote! { Some(<<#ty as #microrm_ref::entity::EntityID>::Entity as #microrm_ref::entity::Entity>::table_name()) };
  40. fk_column_name = quote! { Some("id") };
  41. }
  42. index += 1;
  43. column_types.push(quote! { pub struct #converted_case (); });
  44. column_impls.push(quote! {
  45. impl #microrm_ref::entity::EntityColumn for #columns_name::#converted_case {
  46. type Entity = #struct_name;
  47. fn sql_type(&self) -> &'static str { <#ty as #microrm_ref::model::Modelable>::column_type() }
  48. fn index(&self) -> usize { #index }
  49. fn name(&self) -> &'static str { #snake_case }
  50. fn fk_table_name(&self) -> Option<&'static str> { #fk_table_name }
  51. fn fk_column_name(&self) -> Option<&'static str> { #fk_column_name }
  52. }
  53. });
  54. column_consts.push(quote! {
  55. #[allow(non_upper_case_globals)]
  56. pub const #converted_case : #columns_name::#converted_case = #columns_name::#converted_case();
  57. });
  58. column_array.push(quote! { & #columns_name::#converted_case() });
  59. }
  60. let columns_array_name = format_ident!(
  61. "{}_COLUMNS",
  62. struct_name.to_string().to_case(Case::ScreamingSnake)
  63. );
  64. quote! {
  65. pub mod #columns_name {
  66. pub struct ID ();
  67. #(#column_types)*
  68. }
  69. impl #microrm_ref::entity::EntityColumn for #columns_name::ID {
  70. type Entity = #struct_name;
  71. fn sql_type(&self) -> &'static str { "integer" }
  72. fn index(&self) -> usize { 0 }
  73. fn name(&self) -> &'static str { "id" }
  74. fn fk_table_name(&self) -> Option<&'static str> { None }
  75. fn fk_column_name(&self) -> Option<&'static str> { None }
  76. }
  77. #(#column_impls)*
  78. impl #struct_name {
  79. pub const ID : #columns_name::ID = #columns_name::ID();
  80. #(#column_consts)*
  81. }
  82. #microrm_ref::re_export::lazy_static::lazy_static!{
  83. static ref #columns_array_name: [&'static dyn #microrm_ref::entity::EntityColumn<Entity = #struct_name>;#index + 1] = {
  84. [ &#columns_name::ID(), #(#column_array),* ]
  85. };
  86. }
  87. }
  88. }
  89. fn derive_id(
  90. input: &DeriveInput,
  91. microrm_ref: &proc_macro2::TokenStream,
  92. ) -> proc_macro2::TokenStream {
  93. let struct_name = &input.ident;
  94. let id_name = format_ident!("{}ID", &input.ident);
  95. quote! {
  96. #[derive(Debug,PartialEq,Clone,Copy,#microrm_ref::re_export::serde::Serialize,#microrm_ref::re_export::serde::Deserialize)]
  97. #[allow(unused)]
  98. pub struct #id_name (i64);
  99. impl #microrm_ref::entity::EntityID for #id_name {
  100. type Entity = #struct_name;
  101. fn from_raw_id(raw: i64) -> Self { Self(raw) }
  102. fn raw_id(&self) -> i64 { self.0 }
  103. }
  104. impl #microrm_ref::model::Modelable for #id_name {
  105. fn bind_to(&self, stmt: &mut #microrm_ref::re_export::sqlite::Statement, col: usize) -> #microrm_ref::re_export::sqlite::Result<()> {
  106. use #microrm_ref::re_export::sqlite::Bindable;
  107. self.0.bind(stmt, col)
  108. }
  109. fn build_from(stmt: &#microrm_ref::re_export::sqlite::Statement, col_offset: usize) -> #microrm_ref::re_export::sqlite::Result<(Self, usize)> where Self: Sized {
  110. stmt.read::<i64>(col_offset).map(|x| (#id_name(x), 1))
  111. }
  112. fn column_type() -> &'static str where Self: Sized {
  113. "integer"
  114. }
  115. }
  116. }
  117. }
  118. pub(crate) fn derive(tokens: TokenStream) -> TokenStream {
  119. let input = parse_macro_input!(tokens as DeriveInput);
  120. let microrm_ref = crate::parse_microrm_ref(&input.attrs);
  121. let struct_name = &input.ident;
  122. let id_name = format_ident!("{}ID", &input.ident);
  123. let table_name = format!("{}", struct_name).to_case(Case::Snake);
  124. let st = match &input.data {
  125. syn::Data::Struct(st) => st,
  126. _ => panic!("Can only use derive(Entity) on structs!"),
  127. };
  128. let fields = match &st.fields {
  129. syn::Fields::Named(fields) => fields,
  130. _ => panic!("Can only use derive(Entity) on non-unit structs with named fields!"),
  131. };
  132. let mut variants = Vec::new();
  133. let mut value_references = Vec::new();
  134. let mut build_clauses = Vec::new();
  135. // let mut foreign_keys = Vec::new();
  136. // let mut foreign_key_impls = Vec::new();
  137. let mut index: usize = 0;
  138. let column_output = derive_columns(&input, &microrm_ref, fields.named.iter());
  139. let id_output = derive_id(&input, &microrm_ref);
  140. for name in fields.named.iter() {
  141. let original_case = name.ident.as_ref().unwrap().clone();
  142. let snake_case = original_case.to_string().to_case(Case::Snake);
  143. if original_case != snake_case {
  144. return quote::quote_spanned!(original_case.span() => compile_error!("Names must be in snake_case")).into();
  145. }
  146. let converted_case =
  147. format_ident!("{}", original_case.to_string().to_case(Case::UpperCamel));
  148. variants.push(converted_case.clone());
  149. let field_name = name.ident.as_ref().unwrap().clone();
  150. value_references.push(quote! { &self. #field_name });
  151. let ty = &name.ty;
  152. index += 1;
  153. build_clauses.push(quote! { #field_name: <#ty as #microrm_ref::model::Modelable>::build_from(stmt, #index)?.0 });
  154. /*
  155. if parse_fk(&name.attrs) {
  156. let fk_struct_name = format_ident!("{}{}ForeignKey", struct_name, converted_case);
  157. let ty = &name.ty;
  158. foreign_keys.push(quote! {
  159. &#fk_struct_name { col: #columns_name::#converted_case }
  160. });
  161. foreign_key_impls.push(quote!{
  162. struct #fk_struct_name {
  163. col: #columns_name
  164. }
  165. impl #microrm_ref::entity::EntityForeignKey<#columns_name> for #fk_struct_name {
  166. /*
  167. fn local_column(&self) -> &#columns_name { &self.col }
  168. fn foreign_table_name(&self) -> &'static str {
  169. <<#ty as #microrm_ref::entity::EntityID>::Entity as #microrm_ref::entity::Entity>::table_name()
  170. }
  171. fn foreign_column_name(&self) -> &'static str {
  172. "id"
  173. }
  174. */
  175. }
  176. });
  177. }
  178. */
  179. }
  180. let columns_array_name = format_ident!(
  181. "{}_COLUMNS",
  182. struct_name.to_string().to_case(Case::ScreamingSnake)
  183. );
  184. let field_count = fields.named.iter().count();
  185. let columns_name = format_ident!("_{}_columns", &input.ident.to_string().to_case(Case::Snake));
  186. quote!{
  187. // Related types for #struct_name
  188. #column_output
  189. #id_output
  190. // Implementations for #struct_name
  191. impl #microrm_ref::entity::Entity for #struct_name {
  192. type ID = #id_name;
  193. type IDColumn = #columns_name::ID;
  194. fn table_name() -> &'static str { #table_name }
  195. fn column_count() -> usize {
  196. // +1 for ID column
  197. #field_count + 1
  198. }
  199. fn visit_values<E, F: FnMut(&dyn #microrm_ref::model::Modelable) -> (Result<(), E>)>(&self, visit: &mut F) -> Result<(),E> {
  200. #(visit(#value_references)?);*;
  201. Ok(())
  202. }
  203. fn build_from(stmt: &#microrm_ref::re_export::sqlite::Statement) -> #microrm_ref::re_export::sqlite::Result<Self> {
  204. Ok(Self {
  205. #(#build_clauses),*
  206. })
  207. }
  208. fn columns() -> &'static [&'static dyn #microrm_ref::entity::EntityColumn<Entity = Self>] {
  209. #columns_array_name.as_ref()
  210. }
  211. fn id_column() -> Self::IDColumn {
  212. Self::ID
  213. }
  214. /*fn foreign_keys() -> &'static [&'static dyn #microrm_ref::entity::EntityForeignKey<Self::Column>] {
  215. &[#(#foreign_keys),*]
  216. }*/
  217. }
  218. // Foreign key struct implementations
  219. // #(#foreign_key_impls)*
  220. }.into()
  221. }