From 8852f0b7090c612f1d04a60becb55bbe56441eda Mon Sep 17 00:00:00 2001 From: Melody Horn / boringcactus Date: Wed, 23 Jun 2021 21:07:56 -0600 Subject: start impling diesel::Queryable for Models --- tosin-macros/src/lib.rs | 97 +++++++++++++++++++++++++++++++++++-------------- 1 file changed, 70 insertions(+), 27 deletions(-) (limited to 'tosin-macros') diff --git a/tosin-macros/src/lib.rs b/tosin-macros/src/lib.rs index c9e352c..131888c 100644 --- a/tosin-macros/src/lib.rs +++ b/tosin-macros/src/lib.rs @@ -18,9 +18,35 @@ pub fn model_derive(input: TokenStream) -> TokenStream { impl_model(&ast) } -// TODO clean all this shit up +struct FieldInfo { + tosin_field: T, + diesel_column: T, + diesel_type: T, +} + +struct FieldsInfo { + tosin_fields: Vec, + diesel_columns: Vec, + diesel_types: Vec, +} -fn to_field_spec(field: &syn::Field) -> (impl quote::ToTokens, impl quote::ToTokens) { +impl std::iter::FromIterator> for FieldsInfo { + fn from_iter>>(iter: I) -> Self { + let mut result = Self { + tosin_fields: vec![], + diesel_columns: vec![], + diesel_types: vec![], + }; + for info in iter { + result.tosin_fields.push(info.tosin_field); + result.diesel_columns.push(info.diesel_column); + result.diesel_types.push(info.diesel_type); + } + result + } +} + +fn to_field_spec(field: &syn::Field) -> FieldInfo { fn parse_type(ty: &str) -> syn::Type { syn::parse_str(ty).unwrap() } @@ -41,42 +67,48 @@ fn to_field_spec(field: &syn::Field) -> (impl quote::ToTokens, impl quote::ToTok }) .collect(); if field_type == &parse_type("Option") { - ( - quote! { ::tosin::db::models::Field::IntField { name: stringify!(#field_name) } }, - quote! { #field_name -> Integer } - ) + FieldInfo { + tosin_field: quote! { ::tosin::db::models::Field::IntField { name: stringify!(#field_name) } }, + diesel_column: quote! { #field_name -> Integer }, + diesel_type: quote! { ::tosin::db::sql_types::Integer }, + } } else if field_type == &parse_type("Id") { // TODO foreign key constraint - ( - quote! { ::tosin::db::models::Field::IntField { name: stringify!(#field_name) } }, - quote! { #field_name -> Integer } - ) + FieldInfo { + tosin_field: quote! { ::tosin::db::models::Field::IntField { name: stringify!(#field_name) } }, + diesel_column: quote! { #field_name -> Integer }, + diesel_type: quote! { ::tosin::db::sql_types::Integer }, + } } else if field_type == &parse_type("usize") { // TODO default - ( - quote! { ::tosin::db::models::Field::IntField { name: stringify!(#field_name) } }, - quote! { #field_name -> Integer } - ) + FieldInfo { + tosin_field: quote! { ::tosin::db::models::Field::IntField { name: stringify!(#field_name) } }, + diesel_column: quote! { #field_name -> Integer }, + diesel_type: quote! { ::tosin::db::sql_types::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! { #field_name -> Text } - ) + FieldInfo { + tosin_field: quote! { ::tosin::db::models::Field::CharField { name: stringify!(#field_name), max_length: Some(#max_length) } }, + diesel_column: quote! { #field_name -> Text }, + diesel_type: quote! { ::tosin::db::sql_types::Text } + } } else { - ( - quote! { ::tosin::db::models::Field::CharField { name: stringify!(#field_name), max_length: None } }, - quote! { #field_name -> Text } - ) + FieldInfo { + tosin_field: quote! { ::tosin::db::models::Field::CharField { name: stringify!(#field_name), max_length: None } }, + diesel_column: quote! { #field_name -> Text }, + diesel_type: quote! { ::tosin::db::sql_types::Text } + } } } else if field_type == &parse_type("time::PrimitiveDateTime") { - ( - quote! { ::tosin::db::models::Field::DateTimeField { name: stringify!(#field_name) } }, - quote! { #field_name -> Timestamp } - ) + FieldInfo { + tosin_field: quote! { ::tosin::db::models::Field::DateTimeField { name: stringify!(#field_name) } }, + diesel_column: quote! { #field_name -> Timestamp }, + diesel_type: quote! { ::tosin::db::sql_types::Timestamp } + } } else { use quote::ToTokens; panic!("can't handle {}", field.to_token_stream()) @@ -91,7 +123,8 @@ fn impl_model(ast: &syn::DeriveInput) -> TokenStream { } else { panic!("not on a struct"); }; - let (tosin_fields, diesel_columns): (Vec<_>, Vec<_>) = ast_data.fields.iter().map(to_field_spec).unzip(); + let real_types: Vec<_> = ast_data.fields.iter().map(|field| &field.ty).collect(); + let FieldsInfo { tosin_fields, diesel_columns, diesel_types } = ast_data.fields.iter().map(to_field_spec).collect(); let gen = quote! { impl #name { pub const META: ::tosin::db::models::ModelMeta = ::tosin::db::models::ModelMeta { @@ -100,6 +133,16 @@ fn impl_model(ast: &syn::DeriveInput) -> TokenStream { }; } + impl<__DB: tosin::db::diesel_backend::Backend, __ST> tosin::db::Queryable<__ST, __DB> for #name + where (#(#real_types),*): tosin::db::Queryable<__ST, __DB> { + type Row = <(#(#real_types),*) as tosin::db::Queryable<__ST, __DB>>::Row; + + fn build(row: Self::Row) -> Self { + let row: (#(#real_types),*) = tosin::db::Queryable::build(row); + todo!() + } + } + // this means users need #[macro_use] extern crate diesel; but fuck doing it ourselves table! { #lowercase_name { -- cgit v1.2.3