lib.rs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316
  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. ///
  40. /// The following are understood on individual fields:
  41. /// - `#[microrm_foreign]`: this is a foreign key (and the field must be of a type implementing `EntityID`)
  42. #[proc_macro_derive(Entity, attributes(microrm_internal, microrm_foreign))]
  43. pub fn derive_entity(tokens: TokenStream) -> TokenStream {
  44. let input = parse_macro_input!(tokens as DeriveInput);
  45. let microrm_ref = parse_microrm_ref(&input.attrs);
  46. let struct_name = &input.ident;
  47. let enum_name = format_ident!("{}Columns", &input.ident);
  48. let id_name = format_ident!("{}ID", &input.ident);
  49. let table_name = format!("{}", struct_name).to_case(Case::Snake);
  50. let st = match input.data {
  51. syn::Data::Struct(st) => st,
  52. _ => panic!("Can only use derive(Entity) on structs!"),
  53. };
  54. let fields = match st.fields {
  55. syn::Fields::Named(fields) => fields,
  56. _ => panic!("Can only use derive(Entity) on non-unit structs with named fields!"),
  57. };
  58. let mut variants = Vec::new();
  59. let mut field_names = Vec::new();
  60. let mut field_numbers = Vec::new();
  61. let mut value_references = Vec::new();
  62. let mut foreign_keys = Vec::new();
  63. let mut foreign_key_impls = Vec::new();
  64. for name in fields.named.iter() {
  65. let converted_case =
  66. format!("{}", name.ident.as_ref().unwrap().clone()).to_case(Case::UpperCamel);
  67. let converted_case = format_ident!("{}", converted_case);
  68. variants.push(converted_case.clone());
  69. let field_name = name.ident.as_ref().unwrap().clone();
  70. let field_name_str = format!("{}", field_name);
  71. field_names.push(quote! { Self::Column::#converted_case => #field_name_str });
  72. let nn = field_numbers.len() + 1;
  73. field_numbers.push(quote! { #nn => Self::#converted_case, });
  74. if parse_fk(&name.attrs) {
  75. let fk_struct_name = format_ident!("{}{}ForeignKey", struct_name, converted_case);
  76. let ty = &name.ty;
  77. foreign_keys.push(quote!{
  78. &#fk_struct_name { col: #enum_name::#converted_case }
  79. });
  80. foreign_key_impls.push(quote!{
  81. struct #fk_struct_name {
  82. col: #enum_name
  83. }
  84. impl #microrm_ref::model::EntityForeignKey<#enum_name> for #fk_struct_name {
  85. fn local_column(&self) -> &#enum_name { &self.col }
  86. fn foreign_table_name(&self) -> &'static str {
  87. <<#ty as #microrm_ref::model::EntityID>::Entity as #microrm_ref::model::Entity>::table_name()
  88. }
  89. fn foreign_column_name(&self) -> &'static str {
  90. "id"
  91. }
  92. }
  93. });
  94. }
  95. value_references.push(quote! { &self. #field_name });
  96. }
  97. let field_count = fields.named.iter().count();
  98. quote!{
  99. // Related types for #struct_name
  100. #[derive(Clone,Copy,PartialEq,Hash)]
  101. #[allow(unused)]
  102. #[repr(usize)]
  103. pub enum #enum_name {
  104. ID,
  105. #(#variants),*
  106. }
  107. #[derive(Debug,PartialEq,Clone,Copy,#microrm_ref::re_export::serde::Serialize,#microrm_ref::re_export::serde::Deserialize)]
  108. #[allow(unused)]
  109. pub struct #id_name (i64);
  110. // Implementations for related types
  111. impl #microrm_ref::model::EntityColumns for #enum_name {
  112. type Entity = #struct_name;
  113. }
  114. impl std::convert::From<usize> for #enum_name {
  115. fn from(i: usize) -> Self {
  116. match i {
  117. 0 => Self::ID,
  118. #(#field_numbers)*
  119. _ => {
  120. panic!("Given invalid usize to convert to column")
  121. },
  122. }
  123. }
  124. }
  125. impl #microrm_ref::model::EntityID for #id_name {
  126. type Entity = #struct_name;
  127. fn from_raw_id(raw: i64) -> Self { Self(raw) }
  128. fn raw_id(&self) -> i64 { self.0 }
  129. }
  130. impl #microrm_ref::model::Modelable for #id_name {
  131. fn bind_to(&self, stmt: &mut #microrm_ref::re_export::sqlite::Statement, col: usize) -> #microrm_ref::re_export::sqlite::Result<()> {
  132. use #microrm_ref::re_export::sqlite::Bindable;
  133. self.0.bind(stmt, col)
  134. }
  135. fn build_from(stmt: &#microrm_ref::re_export::sqlite::Statement, col_offset: usize) -> #microrm_ref::re_export::sqlite::Result<(Self, usize)> where Self: Sized {
  136. stmt.read::<i64>(col_offset).map(|x| (#id_name(x), 1))
  137. }
  138. }
  139. // Implementations for #struct_name
  140. impl #microrm_ref::model::Entity for #struct_name {
  141. type Column = #enum_name;
  142. type ID = #id_name;
  143. fn table_name() -> &'static str { #table_name }
  144. fn column_count() -> usize {
  145. // +1 for ID column
  146. #field_count + 1
  147. }
  148. fn index(c: Self::Column) -> usize {
  149. c as usize
  150. }
  151. fn name(c: Self::Column) -> &'static str {
  152. match c {
  153. Self::Column::ID => "ID",
  154. #(#field_names),*
  155. }
  156. }
  157. fn values(&self) -> Vec<&dyn #microrm_ref::model::Modelable> {
  158. vec![ #(#value_references),* ]
  159. }
  160. fn foreign_keys() -> &'static [&'static dyn #microrm_ref::model::EntityForeignKey<Self::Column>] {
  161. &[#(#foreign_keys),*]
  162. }
  163. }
  164. // Foreign key struct implementations
  165. #(#foreign_key_impls)*
  166. }.into()
  167. }
  168. /// Marks a struct as able to be directly used in an Entity to correspond to a single database column.
  169. #[proc_macro_derive(Modelable, attributes(microrm_internal))]
  170. pub fn derive_modelable(tokens: TokenStream) -> TokenStream {
  171. let input = parse_macro_input!(tokens as DeriveInput);
  172. let microrm_ref = parse_microrm_ref(&input.attrs);
  173. let ident = input.ident;
  174. quote!{
  175. impl #microrm_ref::model::Modelable for #ident {
  176. fn bind_to(&self, stmt: &mut #microrm_ref::re_export::sqlite::Statement, col: usize) -> #microrm_ref::re_export::sqlite::Result<()> {
  177. use #microrm_ref::re_export::sqlite;
  178. use #microrm_ref::model::Modelable;
  179. serde_json::to_string(self).expect("can be serialized").bind_to(stmt, col)
  180. }
  181. fn build_from(stmt: &#microrm_ref::re_export::sqlite::Statement, col_offset: usize) -> #microrm_ref::re_export::sqlite::Result<(Self,usize)> {
  182. use #microrm_ref::re_export::sqlite;
  183. use #microrm_ref::model::Modelable;
  184. let str_data = stmt.read::<String>(col_offset).map_err(|e| sqlite::Error { code: None, message: Some(e.to_string()) })?;
  185. let data = serde_json::from_str(str_data.as_str()).map_err(|e| sqlite::Error { code: None, message: Some(e.to_string()) })?;
  186. Ok((data,1))
  187. }
  188. }
  189. }.into()
  190. }
  191. type ColumnList = syn::punctuated::Punctuated::<syn::TypePath, syn::Token![,]>;
  192. struct MakeIndexParams {
  193. unique: Option<syn::Token![!]>,
  194. name: syn::Ident,
  195. #[allow(dead_code)]
  196. comma: syn::Token![,],
  197. columns: ColumnList
  198. }
  199. impl syn::parse::Parse for MakeIndexParams {
  200. fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
  201. Ok(Self {
  202. unique: input.parse()?,
  203. name: input.parse()?,
  204. comma: input.parse()?,
  205. columns: ColumnList::parse_separated_nonempty(input)?
  206. })
  207. }
  208. }
  209. fn do_make_index(tokens: TokenStream, microrm_ref: proc_macro2::TokenStream) -> TokenStream {
  210. let input = parse_macro_input!(tokens as MakeIndexParams);
  211. let index_struct_name = input.name;
  212. let first_col = input.columns.first().unwrap();
  213. let mut column_type_path = first_col.path.clone();
  214. // remove variant name
  215. column_type_path.segments.pop();
  216. let last = column_type_path.segments.pop().expect("Full path to EntityColumn variant");
  217. column_type_path.segments.push(last.value().clone());
  218. let index_entity_type_name = format_ident!("{}Entity", index_struct_name);
  219. let columns = input.columns.clone().into_iter();
  220. let index_sql_name = format!("{}", index_struct_name).to_case(Case::Snake);
  221. let unique = input.unique.is_some();
  222. quote!{
  223. pub struct #index_struct_name {}
  224. type #index_entity_type_name = <#column_type_path as #microrm_ref::model::EntityColumns>::Entity;
  225. impl #microrm_ref::model::Index for #index_struct_name {
  226. type IndexedEntity = #index_entity_type_name;
  227. fn index_name() -> &'static str {
  228. #index_sql_name
  229. }
  230. fn columns() -> &'static [#column_type_path] where Self: Sized {
  231. &[#(#columns),*]
  232. }
  233. fn unique() -> bool where Self: Sized {
  234. #unique
  235. }
  236. }
  237. }.into()
  238. }
  239. /// Defines a struct to represent a optionally-unique index on a table.
  240. ///
  241. /// Suppose the following `Entity` definition is used:
  242. ///
  243. /// ```ignore
  244. /// #[derive(Entity,Serialize,Deserialize)]
  245. /// struct SystemUser {
  246. /// username: String,
  247. /// hashed_password: String
  248. /// }
  249. /// ```
  250. ///
  251. /// We can now use `make_index!` to define an index on the username field:
  252. /// ```ignore
  253. /// make_index!(SystemUsernameIndex, SystemUserColumns::Username)
  254. /// ```
  255. ///
  256. /// This index can be made unique by adding a `!` prior to the type name, as:
  257. /// ```ignore
  258. /// make_index!(!SystemUsernameUniqueIndex, SystemUserColumns::Username)
  259. /// ```
  260. #[proc_macro]
  261. pub fn make_index(tokens: TokenStream) -> TokenStream {
  262. do_make_index(tokens, quote!{ ::microrm })
  263. }
  264. /// For internal use inside the microrm library. See `make_index`.
  265. #[proc_macro]
  266. pub fn make_index_internal(tokens: TokenStream) -> TokenStream {
  267. do_make_index(tokens, quote!{ crate })
  268. }
  269. // , attributes(microrm_internal))]