From 88d527d574567420d7aa72f37205bd6b345b8dd7 Mon Sep 17 00:00:00 2001 From: Melody Horn / boringcactus Date: Sat, 19 Jun 2021 23:10:53 -0600 Subject: generate diesel::table! in derive(Model) --- tosin-macros/src/lib.rs | 50 ++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 39 insertions(+), 11 deletions(-) (limited to 'tosin-macros/src/lib.rs') diff --git a/tosin-macros/src/lib.rs b/tosin-macros/src/lib.rs index 7028274..c9e352c 100644 --- a/tosin-macros/src/lib.rs +++ b/tosin-macros/src/lib.rs @@ -18,7 +18,9 @@ pub fn model_derive(input: TokenStream) -> TokenStream { impl_model(&ast) } -fn to_field_spec(field: &syn::Field) -> impl quote::ToTokens { +// TODO clean all this shit up + +fn to_field_spec(field: &syn::Field) -> (impl quote::ToTokens, impl quote::ToTokens) { fn parse_type(ty: &str) -> syn::Type { syn::parse_str(ty).unwrap() } @@ -39,24 +41,42 @@ fn to_field_spec(field: &syn::Field) -> impl quote::ToTokens { }) .collect(); if field_type == &parse_type("Option") { - quote! { ::tosin::db::models::Field::IntField { name: stringify!(#field_name) } } + ( + quote! { ::tosin::db::models::Field::IntField { name: stringify!(#field_name) } }, + quote! { #field_name -> Integer } + ) } else if field_type == &parse_type("Id") { // TODO foreign key constraint - quote! { ::tosin::db::models::Field::IntField { name: stringify!(#field_name) } } + ( + quote! { ::tosin::db::models::Field::IntField { name: stringify!(#field_name) } }, + quote! { #field_name -> Integer } + ) } else if field_type == &parse_type("usize") { // TODO default - quote! { ::tosin::db::models::Field::IntField { name: stringify!(#field_name) } } + ( + quote! { ::tosin::db::models::Field::IntField { name: stringify!(#field_name) } }, + quote! { #field_name -> Integer } + ) } 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) } } + ( + quote! { ::tosin::db::models::Field::CharField { name: stringify!(#field_name), max_length: Some(#max_length) } }, + quote! { #field_name -> Text } + ) } else { - quote! { ::tosin::db::models::Field::CharField { name: stringify!(#field_name), max_length: None } } + ( + quote! { ::tosin::db::models::Field::CharField { name: stringify!(#field_name), max_length: None } }, + quote! { #field_name -> Text } + ) } } else if field_type == &parse_type("time::PrimitiveDateTime") { - quote! { ::tosin::db::models::Field::DateTimeField { name: stringify!(#field_name) } } + ( + quote! { ::tosin::db::models::Field::DateTimeField { name: stringify!(#field_name) } }, + quote! { #field_name -> Timestamp } + ) } else { use quote::ToTokens; panic!("can't handle {}", field.to_token_stream()) @@ -65,19 +85,27 @@ fn to_field_spec(field: &syn::Field) -> impl quote::ToTokens { 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) + let lowercase_name = quote::format_ident!("{}", name.to_string().to_lowercase()); + let ast_data = if let syn::Data::Struct(ast_data) = &ast.data { + ast_data } else { panic!("not on a struct"); }; + let (tosin_fields, diesel_columns): (Vec<_>, Vec<_>) = ast_data.fields.iter().map(to_field_spec).unzip(); let gen = quote! { impl #name { pub const META: ::tosin::db::models::ModelMeta = ::tosin::db::models::ModelMeta { name: stringify!(#name), - fields: &[ #(#fields),* ], + fields: &[ #(#tosin_fields),* ], }; } + + // this means users need #[macro_use] extern crate diesel; but fuck doing it ourselves + table! { + #lowercase_name { + #(#diesel_columns,)* + } + } }; gen.into() } -- cgit v1.2.3