diff options
author | Melody Horn / boringcactus <melody@boringcactus.com> | 2021-06-23 21:07:56 -0600 |
---|---|---|
committer | Melody Horn / boringcactus <melody@boringcactus.com> | 2021-06-23 21:07:56 -0600 |
commit | 8852f0b7090c612f1d04a60becb55bbe56441eda (patch) | |
tree | f9fe2c8d3782ad2c09bb9fa4171ecb0653cc92b0 /tosin-macros | |
parent | 88d527d574567420d7aa72f37205bd6b345b8dd7 (diff) | |
download | tosin-8852f0b7090c612f1d04a60becb55bbe56441eda.tar.gz tosin-8852f0b7090c612f1d04a60becb55bbe56441eda.zip |
start impling diesel::Queryable for Models
Diffstat (limited to 'tosin-macros')
-rw-r--r-- | tosin-macros/src/lib.rs | 97 |
1 files changed, 70 insertions, 27 deletions
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<T: quote::ToTokens> { + tosin_field: T, + diesel_column: T, + diesel_type: T, +} + +struct FieldsInfo<T: quote::ToTokens> { + tosin_fields: Vec<T>, + diesel_columns: Vec<T>, + diesel_types: Vec<T>, +} -fn to_field_spec(field: &syn::Field) -> (impl quote::ToTokens, impl quote::ToTokens) { +impl<T: quote::ToTokens> std::iter::FromIterator<FieldInfo<T>> for FieldsInfo<T> { + fn from_iter<I: IntoIterator<Item=FieldInfo<T>>>(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<impl quote::ToTokens> { 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<Id>") { - ( - 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 { |