lib.rs 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192
  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_microrm_ref(attrs: &[syn::Attribute]) -> proc_macro2::TokenStream {
  6. for attr in attrs {
  7. if attr.path.segments.is_empty() {
  8. continue;
  9. }
  10. if attr.tokens.is_empty() && attr.path.segments.last().unwrap().ident == "microrm_internal"
  11. {
  12. return quote! { crate };
  13. }
  14. }
  15. quote! { ::microrm }
  16. }
  17. fn parse_fk(attrs: &[syn::Attribute]) -> bool {
  18. for attr in attrs {
  19. if attr.path.segments.len() == 1 && attr.path.segments.last().unwrap().ident == "microrm_foreign" {
  20. return true
  21. }
  22. }
  23. false
  24. }
  25. /// Turns a serializable/deserializable struct into a microrm entity model.
  26. ///
  27. /// There are two important visible effects:
  28. /// - Provides an implementation of `microrm::model::Entity`
  29. /// - Defines a <struct-name>Columns enum
  30. ///
  31. /// Note that names are converted from CamelCase to snake_case and vice versa
  32. /// where applicable, so a struct named `TestModel` is given a table name `test_model`
  33. /// and a struct field named `field_name` is given a variant name of `FieldName`.
  34. ///
  35. /// The `#[microrm...]` attributes can be used to control the derivation somewhat.
  36. /// The following are understood for the Entity struct:
  37. /// - `#[microrm_internal]`: this is internal to the microrm crate (of extremely limited usefulness
  38. /// outside the microrm library)
  39. /// The following are understood on individual fields
  40. /// - `#[microrm_foreign]`: this is a foreign key (and the field must be of a type implementing `EntityID`)
  41. #[proc_macro_derive(Entity, attributes(microrm_internal, microrm_foreign))]
  42. pub fn derive_entity(tokens: TokenStream) -> TokenStream {
  43. let input = parse_macro_input!(tokens as DeriveInput);
  44. let microrm_ref = parse_microrm_ref(&input.attrs);
  45. let struct_name = &input.ident;
  46. let enum_name = format_ident!("{}Columns", &input.ident);
  47. let id_name = format_ident!("{}ID", &input.ident);
  48. let table_name = format!("{}", struct_name).to_case(Case::Snake);
  49. let st = match input.data {
  50. syn::Data::Struct(st) => st,
  51. _ => panic!("Can only use derive(Entity) on structs!"),
  52. };
  53. let fields = match st.fields {
  54. syn::Fields::Named(fields) => fields,
  55. _ => panic!("Can only use derive(Entity) on non-unit structs with named fields!"),
  56. };
  57. let mut variants = Vec::new();
  58. let mut field_names = Vec::new();
  59. let mut value_references = Vec::new();
  60. let mut foreign_keys = Vec::new();
  61. let mut foreign_key_impls = Vec::new();
  62. for name in fields.named.iter() {
  63. let converted_case =
  64. format!("{}", name.ident.as_ref().unwrap().clone()).to_case(Case::UpperCamel);
  65. let converted_case = format_ident!("{}", converted_case);
  66. variants.push(converted_case.clone());
  67. let field_name = name.ident.as_ref().unwrap().clone();
  68. let field_name_str = format!("{}", field_name);
  69. field_names.push(quote! { Self::Column::#converted_case => #field_name_str });
  70. if parse_fk(&name.attrs) {
  71. let fk_struct_name = format_ident!("{}{}ForeignKey", struct_name, converted_case);
  72. let ty = &name.ty;
  73. foreign_keys.push(quote!{
  74. &#fk_struct_name { col: #enum_name::#converted_case }
  75. });
  76. foreign_key_impls.push(quote!{
  77. struct #fk_struct_name {
  78. col: #enum_name
  79. }
  80. impl #microrm_ref::model::EntityForeignKey<#enum_name> for #fk_struct_name {
  81. fn local_column(&self) -> &#enum_name { &self.col }
  82. fn foreign_table_name(&self) -> &'static str {
  83. <<#ty as #microrm_ref::model::EntityID>::Entity as #microrm_ref::model::Entity>::table_name()
  84. }
  85. fn foreign_column_name(&self) -> &'static str {
  86. "id"
  87. }
  88. }
  89. });
  90. }
  91. value_references.push(quote! { &self. #field_name });
  92. }
  93. let field_count = fields.named.iter().count();
  94. quote!{
  95. // Related types for #struct_name
  96. #[derive(Clone,Copy)]
  97. #[allow(unused)]
  98. pub enum #enum_name {
  99. ID,
  100. #(#variants),*
  101. }
  102. #[derive(Debug,PartialEq,Clone,Copy,#microrm_ref::re_export::serde::Serialize,#microrm_ref::re_export::serde::Deserialize)]
  103. #[allow(unused)]
  104. pub struct #id_name (i64);
  105. // Implementations for related types
  106. impl #microrm_ref::model::EntityColumns for #enum_name {
  107. type Entity = #struct_name;
  108. }
  109. impl #microrm_ref::model::EntityID for #id_name {
  110. type Entity = #struct_name;
  111. fn from_raw_id(raw: i64) -> Self { Self(raw) }
  112. fn raw_id(&self) -> i64 { self.0 }
  113. }
  114. impl #microrm_ref::re_export::rusqlite::ToSql for #id_name {
  115. fn to_sql(&self) -> #microrm_ref::re_export::rusqlite::Result<#microrm_ref::re_export::rusqlite::types::ToSqlOutput<'_>> {
  116. self.0.to_sql()
  117. }
  118. }
  119. // Implementations for #struct_name
  120. impl #microrm_ref::model::Entity for #struct_name {
  121. type Column = #enum_name;
  122. type ID = #id_name;
  123. fn table_name() -> &'static str { #table_name }
  124. fn column_count() -> usize {
  125. // +1 for ID column
  126. #field_count + 1
  127. }
  128. fn index(c: Self::Column) -> usize {
  129. c as usize
  130. }
  131. fn name(c: Self::Column) -> &'static str {
  132. match c {
  133. Self::Column::ID => "ID",
  134. #(#field_names),*
  135. }
  136. }
  137. fn values(&self) -> Vec<&dyn #microrm_ref::re_export::rusqlite::ToSql> {
  138. vec![ #(#value_references),* ]
  139. }
  140. fn foreign_keys() -> &'static [&'static dyn #microrm_ref::model::EntityForeignKey<Self::Column>] {
  141. &[#(#foreign_keys),*]
  142. }
  143. }
  144. #(#foreign_key_impls)*
  145. }.into()
  146. }
  147. /// Marks a struct as able to be directly used in an Entity to correspond to a single database column.
  148. #[proc_macro_derive(Modelable, attributes(microrm_internal))]
  149. pub fn derive_modelable(tokens: TokenStream) -> TokenStream {
  150. let input = parse_macro_input!(tokens as DeriveInput);
  151. let microrm_ref = parse_microrm_ref(&input.attrs);
  152. let ident = input.ident;
  153. quote!{
  154. impl #microrm_ref::re_export::rusqlite::ToSql for #ident {
  155. fn to_sql(&self) -> #microrm_ref::re_export::rusqlite::Result<#microrm_ref::re_export::rusqlite::types::ToSqlOutput<'_>> {
  156. use #microrm_ref::re_export::rusqlite::types::{ToSqlOutput,Value};
  157. Ok(ToSqlOutput::Owned(Value::Text(#microrm_ref::re_export::serde_json::to_string(self).expect("can be serialized"))))
  158. }
  159. }
  160. }.into()
  161. }