aboutsummaryrefslogtreecommitdiff
path: root/tosin-macros
diff options
context:
space:
mode:
authorMelody Horn / boringcactus <melody@boringcactus.com>2021-06-23 21:07:56 -0600
committerMelody Horn / boringcactus <melody@boringcactus.com>2021-06-23 21:07:56 -0600
commit8852f0b7090c612f1d04a60becb55bbe56441eda (patch)
treef9fe2c8d3782ad2c09bb9fa4171ecb0653cc92b0 /tosin-macros
parent88d527d574567420d7aa72f37205bd6b345b8dd7 (diff)
downloadtosin-8852f0b7090c612f1d04a60becb55bbe56441eda.tar.gz
tosin-8852f0b7090c612f1d04a60becb55bbe56441eda.zip
start impling diesel::Queryable for Models
Diffstat (limited to 'tosin-macros')
-rw-r--r--tosin-macros/src/lib.rs97
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 {