Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Split ModelV2 codegen in multiple submodules (part 1) #7254

Merged
merged 6 commits into from
Apr 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 24 additions & 10 deletions editoast/editoast_derive/src/modelv2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,23 +19,37 @@ pub fn model(input: &DeriveInput) -> Result<TokenStream> {
let model_name = &input.ident;
let model_vis = &input.vis;
let options = ModelArgs::from_derive_input(input)?;
let config = ModelConfig::from_macro_args(options, model_name.clone())?;
let config = ModelConfig::from_macro_args(options, model_name.clone(), model_vis.clone())?;

let identifiers_impls = config.make_identifiers_impls();
let model_decl = config.make_model_decl(model_vis);
let from_impls = config.make_from_impls();
let model_impl = config.model_impl();
let row_decl = config.row_decl();
let changeset_decl = config.changeset_decl();

let cs_builder = config.make_builder(true);
let patch_builder = config.make_builder(false);
let identifiable_impls = config.identifiable_impls();
let preferred_id_impl = config.preferred_id_impl();

let model_from_row_impl = config.model_from_row_impl();
let changeset_from_model_impl = config.changeset_from_model_impl();

let changeset_builder = config.changeset_builder_impl_block();
let patch_builder = config.patch_builder_impl_block();

let model_impls = config.make_model_traits_impl();

Ok(quote! {
#identifiers_impls
#model_decl
#from_impls
#cs_builder
#model_impl
#row_decl
#changeset_decl

#(#identifiable_impls)*
#preferred_id_impl

#model_from_row_impl
#changeset_from_model_impl

#changeset_builder
#patch_builder

#model_impls
})
}
Expand Down
8 changes: 8 additions & 0 deletions editoast/editoast_derive/src/modelv2/args.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,4 +68,12 @@ impl GeneratedTypeArgs {
pub fn ident(&self) -> syn::Ident {
syn::Ident::new(self.type_name.as_ref().unwrap(), Span::call_site())
}

pub fn visibility(&self) -> syn::Visibility {
if self.public {
syn::Visibility::Public(Default::default())
} else {
syn::Visibility::Inherited
}
}
}
257 changes: 109 additions & 148 deletions editoast/editoast_derive/src/modelv2/codegen.rs
Original file line number Diff line number Diff line change
@@ -1,173 +1,134 @@
mod changeset_builder_impl_block;
mod changeset_decl;
mod changeset_from_model;
mod identifiable_impl;
mod model_from_row_impl;
mod model_impl;
mod preferred_id_impl;
mod row_decl;

use proc_macro2::{Span, TokenStream};
use quote::quote;

use crate::modelv2::codegen::changeset_decl::ChangesetDecl;
use crate::modelv2::codegen::changeset_decl::ChangesetFieldDecl;
use crate::modelv2::codegen::model_impl::ModelImpl;
use crate::modelv2::codegen::row_decl::RowDecl;
use crate::modelv2::codegen::row_decl::RowFieldDecl;

use self::changeset_builder_impl_block::BuilderType;
use self::changeset_builder_impl_block::ChangesetBuilderImplBlock;
use self::changeset_from_model::ChangesetFromModelImpl;
use self::identifiable_impl::IdentifiableImpl;
use self::model_from_row_impl::ModelFromRowImpl;
use self::preferred_id_impl::PreferredIdImpl;

use super::utils::np;
use super::Identifier;
use super::ModelConfig;

impl ModelConfig {
pub fn make_model_decl(&self, vis: &syn::Visibility) -> TokenStream {
let model = &self.model;
let table = &self.table;
let np!(field, ty, column): np!(vec3) = self
.iter_fields()
.map(|field| np!(&field.ident, field.transform_type(), &field.column))
.unzip();
let np!(cs_field, cs_ty, cs_column): np!(vec3) = self
.iter_fields()
.filter(|f| !self.is_primary(f))
.map(|field| np!(&field.ident, field.transform_type(), &field.column))
.unzip();
let cs_ident = self.changeset.ident();
let cs_derive = &self.changeset.derive;
let cs_pub = if self.changeset.public {
quote! { pub }
} else {
quote! {}
};
let row_ident = self.row.ident();
let row_derive = &self.row.derive;
let row_pub = if self.row.public {
quote! { pub }
} else {
quote! {}
};
quote! {
#[automatically_derived]
impl crate::modelsv2::Model for #model {
type Row = #row_ident;
type Changeset = #cs_ident;
}
pub(crate) fn model_impl(&self) -> ModelImpl {
ModelImpl {
model: self.model.clone(),
row: self.row.ident(),
changeset: self.changeset.ident(),
}
}

#[derive(Queryable, QueryableByName, #(#row_derive),*)]
#[diesel(table_name = #table)]
#vis struct #row_ident {
#(#[diesel(column_name = #column)] #row_pub #field: #ty),*
}
pub(crate) fn row_decl(&self) -> RowDecl {
RowDecl {
vis: self.visibility.clone(),
ident: self.row.ident(),
table: self.table.clone(),
additional_derives: self.row.derive.clone(),
fields: self
.iter_fields()
.map(|field| RowFieldDecl {
vis: self.row.visibility(),
name: field.ident.clone(),
ty: field.transform_type(),
column: field.column.clone(),
})
.collect(),
}
}

#[derive(Default, Queryable, AsChangeset, Insertable, #(#cs_derive),*)]
#[diesel(table_name = #table)]
#vis struct #cs_ident {
#(#[diesel(deserialize_as = #cs_ty, column_name = #cs_column)] #cs_pub #cs_field: Option<#cs_ty>),*
}
pub(crate) fn changeset_decl(&self) -> ChangesetDecl {
ChangesetDecl {
vis: self.visibility.clone(),
ident: self.changeset.ident(),
table: self.table.clone(),
additional_derives: self.changeset.derive.clone(),
fields: self
.iter_fields()
.filter(|f| !self.is_primary(f))
.map(|field| ChangesetFieldDecl {
vis: self.changeset.visibility(),
name: field.ident.clone(),
ty: field.transform_type(),
column: field.column.clone(),
})
.collect(),
}
}

pub fn make_builder(&self, changeset: bool) -> TokenStream {
let np!(fields, fns, flat_fns, types, bodies, flat_bodies): np!(vec6) = self
.iter_fields()
.filter(|f| !self.is_primary(f))
.filter(|field| !field.builder_skip)
.map(|field| {
let ident = &field.ident;
let expr = field.into_transformed(quote! { #ident });
let body = if changeset {
quote! { self.#ident = Some(#expr) }
} else {
quote! { self.changeset.#ident = Some(#expr) }
};
let flat_body = if changeset {
quote! { self.#ident = #ident.map(|#ident| #expr) }
} else {
quote! { self.changeset.#ident = #ident.map(|#ident| #expr) }
};
np!(
ident,
&field.builder_ident,
syn::Ident::new(&format!("flat_{}", &field.builder_ident), Span::call_site()),
&field.ty,
body,
flat_body
)
})
.unzip();
pub(crate) fn changeset_builder_impl_block(&self) -> ChangesetBuilderImplBlock {
ChangesetBuilderImplBlock {
builder_type: BuilderType::Changeset,
model: self.model.clone(),
changeset: self.changeset.ident(),
fields: self
.iter_fields()
.filter(|field| !self.is_primary(field))
.filter(|field| !field.builder_skip)
.cloned()
.collect(),
}
}

let impl_decl = if changeset {
let tn = self.changeset.ident();
quote! { impl #tn }
} else {
let tn = &self.model;
quote! { impl<'a> crate::modelsv2::Patch<'a, #tn> }
};
pub(crate) fn patch_builder_impl_block(&self) -> ChangesetBuilderImplBlock {
let mut builder = self.changeset_builder_impl_block();
builder.builder_type = BuilderType::Patch;
builder
}

quote! {
#[automatically_derived]
#impl_decl {
#(
#[allow(unused)]
#[must_use = "builder methods are intended to be chained"]
pub fn #fns(mut self, #fields: #types) -> Self {
#bodies;
self
}
pub(crate) fn identifiable_impls(&self) -> Vec<IdentifiableImpl> {
self.identifiers
.iter()
.map(|identifier| IdentifiableImpl {
model: self.model.clone(),
ty: identifier.type_expr(self),
fields: identifier.get_idents(),
})
.collect()
}

#[allow(unused)]
#[must_use = "builder methods are intended to be chained"]
pub fn #flat_fns(mut self, #fields: Option<#types>) -> Self {
#flat_bodies;
self
}
)*
}
pub(crate) fn preferred_id_impl(&self) -> PreferredIdImpl {
PreferredIdImpl {
model: self.model.clone(),
ty: self.preferred_identifier.type_expr(self),
}
}

pub fn make_identifiers_impls(&self) -> TokenStream {
let model = &self.model;
let (idents, ty): (Vec<_>, Vec<_>) = self
.identifiers
.iter()
.map(|id| (id.get_idents(), id.type_expr(self)))
.unzip();
let prefer_ty = self.preferred_identifier.type_expr(self);
quote! {
#(#[automatically_derived] impl crate::models::Identifiable<#ty> for #model {
fn get_id(&self) -> #ty {
(#(self.#idents.clone()),*)
}
})*
#[automatically_derived]
impl crate::models::PreferredId<#prefer_ty> for #model {}
pub(crate) fn model_from_row_impl(&self) -> ModelFromRowImpl {
ModelFromRowImpl {
model: self.model.clone(),
row: self.row.ident(),
fields: self.fields.clone(),
}
}

pub fn make_from_impls(&self) -> TokenStream {
let model = &self.model;
let (row_field, row_value): (Vec<_>, Vec<_>) = self
.iter_fields()
.map(|field| {
let ident = &field.ident;
(ident, field.from_transformed(quote! { row.#ident }))
})
.unzip();
let (cs_field, cs_value): (Vec<_>, Vec<_>) = self
.iter_fields()
.filter(|f| !self.is_primary(f))
.map(|field| {
let ident = &field.ident;
(ident, field.into_transformed(quote! { model.#ident }))
})
.unzip();
let row_ident = self.row.ident();
let cs_ident = self.changeset.ident();
quote! {
#[automatically_derived]
impl From<#row_ident> for #model {
fn from(row: #row_ident) -> Self {
Self {
#( #row_field: #row_value ),*
}
}
}

#[automatically_derived]
impl From<#model> for #cs_ident {
fn from(model: #model) -> Self {
Self {
#( #cs_field: Some(#cs_value) ),*
}
}
}
pub(crate) fn changeset_from_model_impl(&self) -> ChangesetFromModelImpl {
ChangesetFromModelImpl {
model: self.model.clone(),
changeset: self.changeset.ident(),
fields: self
.fields
.iter()
.filter(|f| !self.is_primary(f))
.cloned()
.collect(),
}
}

Expand Down
Loading
Loading