aboutsummaryrefslogtreecommitdiff
path: root/tosin-macros
diff options
context:
space:
mode:
authorMelody Horn / boringcactus <melody@boringcactus.com>2021-06-18 15:18:50 -0600
committerMelody Horn / boringcactus <melody@boringcactus.com>2021-06-18 15:18:50 -0600
commite5a744a3c32ccb4520f934595e317d5737bc008c (patch)
tree8f7991d906dd885a09d0907e72dbd1fc32852a7d /tosin-macros
parentacf79a306797b1b12e95415c340792d7c88815ab (diff)
downloadtosin-e5a744a3c32ccb4520f934595e317d5737bc008c.tar.gz
tosin-e5a744a3c32ccb4520f934595e317d5737bc008c.zip
correctly gather model info
Diffstat (limited to 'tosin-macros')
-rw-r--r--tosin-macros/src/lib.rs57
1 files changed, 54 insertions, 3 deletions
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),* ],
};
}
};