diff options
| author | Melody Horn / boringcactus <melody@boringcactus.com> | 2021-06-18 15:18:50 -0600 | 
|---|---|---|
| committer | Melody Horn / boringcactus <melody@boringcactus.com> | 2021-06-18 15:18:50 -0600 | 
| commit | e5a744a3c32ccb4520f934595e317d5737bc008c (patch) | |
| tree | 8f7991d906dd885a09d0907e72dbd1fc32852a7d | |
| parent | acf79a306797b1b12e95415c340792d7c88815ab (diff) | |
| download | tosin-e5a744a3c32ccb4520f934595e317d5737bc008c.tar.gz tosin-e5a744a3c32ccb4520f934595e317d5737bc008c.zip  | |
correctly gather model info
| -rw-r--r-- | tests/tutorial/mod.rs | 4 | ||||
| -rw-r--r-- | tosin-macros/src/lib.rs | 57 | 
2 files changed, 56 insertions, 5 deletions
diff --git a/tests/tutorial/mod.rs b/tests/tutorial/mod.rs index fb05066..b26aa39 100644 --- a/tests/tutorial/mod.rs +++ b/tests/tutorial/mod.rs @@ -277,9 +277,9 @@ fn settings() -> Settings<impl Connectable> {  tosin::main!(urls(), settings());      "#).unwrap(); -    // cargo run make-migrations polls +    // cargo run make-migrations      Command::new(CARGO) -        .args(&["run", "make-migrations", "polls"]) +        .args(&["run", "make-migrations"])          .status()          .unwrap()          .check(); diff --git a/tosin-macros/src/lib.rs b/tosin-macros/src/lib.rs index 86750e1..7028274 100644 --- a/tosin-macros/src/lib.rs +++ b/tosin-macros/src/lib.rs @@ -2,6 +2,7 @@  extern crate proc_macro; +use std::collections::HashMap;  use std::fs;  use proc_macro::TokenStream; @@ -17,14 +18,64 @@ pub fn model_derive(input: TokenStream) -> TokenStream {      impl_model(&ast)  } +fn to_field_spec(field: &syn::Field) -> impl quote::ToTokens { +    fn parse_type(ty: &str) -> syn::Type { +        syn::parse_str(ty).unwrap() +    } +    let field_name = &field.ident; +    let field_type = &field.ty; +    let model_options: HashMap<syn::Path, syn::Lit> = field.attrs.iter() +        .filter_map(|attr| attr.parse_meta().ok()) +        .filter_map(|meta| if let syn::Meta::List(meta) = meta { Some(meta) } else { None }) +        .filter(|meta| meta.path.get_ident().map_or(false, |path| path == "model")) +        .flat_map(|model| { +            model.nested.into_iter().filter_map(|item| { +                if let syn::NestedMeta::Meta(syn::Meta::NameValue(data)) = item { +                    Some((data.path, data.lit)) +                } else { +                    None +                } +            }) +        }) +        .collect(); +    if field_type == &parse_type("Option<Id>") { +        quote! { ::tosin::db::models::Field::IntField { name: stringify!(#field_name) } } +    } else if field_type == &parse_type("Id") { +        // TODO foreign key constraint +        quote! { ::tosin::db::models::Field::IntField { name: stringify!(#field_name) } } +    } else if field_type == &parse_type("usize") { +        // TODO default +        quote! { ::tosin::db::models::Field::IntField { name: stringify!(#field_name) } } +    } else if field_type == &parse_type("String") { +        let max_length = model_options.iter() +            .find(|(name, _value)| name.get_ident().map_or(false, |path| path == "max_length")) +            .map(|(_name, value)| value); +        if let Some(max_length) = max_length { +            quote! { ::tosin::db::models::Field::CharField { name: stringify!(#field_name), max_length: Some(#max_length) } } +        } else { +            quote! { ::tosin::db::models::Field::CharField { name: stringify!(#field_name), max_length: None } } +        } +    } else if field_type == &parse_type("time::PrimitiveDateTime") { +        quote! { ::tosin::db::models::Field::DateTimeField { name: stringify!(#field_name) } } +    } else { +        use quote::ToTokens; +        panic!("can't handle {}", field.to_token_stream()) +    } +} +  fn impl_model(ast: &syn::DeriveInput) -> TokenStream {      let name = &ast.ident; +    let fields = if let syn::Data::Struct(ast) = &ast.data { +        ast.fields.iter() +            .map(to_field_spec) +    } else { +        panic!("not on a struct"); +    };      let gen = quote! { -        use tosin::db::models::ModelMeta;          impl #name { -            pub const META: ModelMeta = ModelMeta { +            pub const META: ::tosin::db::models::ModelMeta = ::tosin::db::models::ModelMeta {                  name: stringify!(#name), -                fields: &[], +                fields: &[ #(#fields),* ],              };          }      };  |