|
@@ -221,13 +221,110 @@ pub fn derive_entity(tokens: TokenStream) -> TokenStream {
|
|
|
pub fn derive_modelable(tokens: TokenStream) -> TokenStream {
|
|
|
let input = parse_macro_input!(tokens as DeriveInput);
|
|
|
|
|
|
- // TODO: implement unit-only-variant-enum optimization
|
|
|
- // TODO: if is a struct/newtype AND only has single element, store as that type
|
|
|
+ if let syn::Data::Enum(e) = &input.data {
|
|
|
+ return derive_modelable_enum(&input, e)
|
|
|
+ }
|
|
|
+
|
|
|
+ if let syn::Data::Struct(s) = &input.data {
|
|
|
+ if s.fields.len() == 1 {
|
|
|
+ return derive_transparent_struct(&input, s)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ derive_modelable_general(&input)
|
|
|
+}
|
|
|
+
|
|
|
+fn derive_transparent_struct(input: &syn::DeriveInput, ds: &syn::DataStruct) -> TokenStream {
|
|
|
+ // for single-element structs, we can simply store these transparently however the element
|
|
|
+ // would be stored
|
|
|
+ let microrm_ref = parse_microrm_ref(&input.attrs);
|
|
|
+ let struct_name = &input.ident;
|
|
|
+
|
|
|
+ let field = ds.fields.iter().next().unwrap();
|
|
|
+
|
|
|
+ let (bind_to, build_from) =
|
|
|
+ if let Some(i) = &field.ident {
|
|
|
+ (
|
|
|
+ quote!{ self.#i.bind_to(stmt, col) },
|
|
|
+ quote!{ Self { #i: field.0 } }
|
|
|
+ )
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ (
|
|
|
+ quote!{ self.0.bind_to(stmt, col) },
|
|
|
+ quote!{ Self(field.0) }
|
|
|
+ )
|
|
|
+ };
|
|
|
+
|
|
|
+ let field_ty = &field.ty;
|
|
|
+
|
|
|
+ quote! {
|
|
|
+ impl #microrm_ref::model::Modelable for #struct_name {
|
|
|
+ fn bind_to(&self, stmt: &mut #microrm_ref::re_export::sqlite::Statement, col: usize) -> #microrm_ref::re_export::sqlite::Result<()> {
|
|
|
+ #bind_to
|
|
|
+ }
|
|
|
+
|
|
|
+ fn build_from(stmt: &#microrm_ref::re_export::sqlite::Statement, col_offset: usize) -> #microrm_ref::re_export::sqlite::Result<(Self,usize)> {
|
|
|
+ let field = #field_ty::build_from(stmt, col_offset)?;
|
|
|
+ Ok((#build_from, field.1))
|
|
|
+ }
|
|
|
+
|
|
|
+ fn column_type() -> &'static str where Self: Sized {
|
|
|
+ <#field_ty as #microrm_ref::model::Modelable>::column_type()
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }.into()
|
|
|
+}
|
|
|
+
|
|
|
+fn derive_modelable_enum(input: &syn::DeriveInput, de: &syn::DataEnum) -> TokenStream {
|
|
|
+ for variant in &de.variants {
|
|
|
+ if !variant.fields.is_empty() {
|
|
|
+ return derive_modelable_general(input)
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
+ // only unit variants! we can store as a string
|
|
|
let microrm_ref = parse_microrm_ref(&input.attrs);
|
|
|
+ let enum_name = &input.ident;
|
|
|
+
|
|
|
+ let mut variant_names = Vec::new();
|
|
|
+ let mut variant_name_strs = Vec::new();
|
|
|
+
|
|
|
+ for variant in &de.variants {
|
|
|
+ variant_names.push(variant.ident.clone());
|
|
|
+ variant_name_strs.push(variant.ident.to_string());
|
|
|
+ }
|
|
|
+
|
|
|
+ quote! {
|
|
|
+ impl #microrm_ref::model::Modelable for #enum_name {
|
|
|
+ fn bind_to(&self, stmt: &mut #microrm_ref::re_export::sqlite::Statement, col: usize) -> #microrm_ref::re_export::sqlite::Result<()> {
|
|
|
+ match self {
|
|
|
+ #(
|
|
|
+ Self::#variant_names => #variant_name_strs.bind_to(stmt, col)
|
|
|
+ ),*
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
- let ident = input.ident;
|
|
|
+ fn build_from(stmt: &#microrm_ref::re_export::sqlite::Statement, col_offset: usize) -> #microrm_ref::re_export::sqlite::Result<(Self,usize)> {
|
|
|
+ let str_form = String::build_from(stmt, col_offset)?.0;
|
|
|
+ #(
|
|
|
+ if str_form == #variant_name_strs { return Ok((Self::#variant_names, 1)) }
|
|
|
+ )*
|
|
|
+
|
|
|
+ return Err(#microrm_ref::re_export::sqlite::Error { code: None, message: None })
|
|
|
+ }
|
|
|
+
|
|
|
+ fn column_type() -> &'static str where Self: Sized {
|
|
|
+ "text"
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }.into()
|
|
|
+}
|
|
|
+
|
|
|
+fn derive_modelable_general(input: &syn::DeriveInput) -> TokenStream {
|
|
|
+ let microrm_ref = parse_microrm_ref(&input.attrs);
|
|
|
|
|
|
+ let ident = &input.ident;
|
|
|
|
|
|
quote!{
|
|
|
impl #microrm_ref::model::Modelable for #ident {
|