Skip to content

Commit

Permalink
editoast: add ToToken ModelV2 definition ChangesetBuilderImplBlock
Browse files Browse the repository at this point in the history
  • Loading branch information
leovalais committed Apr 18, 2024
1 parent 475c144 commit 6828093
Show file tree
Hide file tree
Showing 3 changed files with 117 additions and 60 deletions.
7 changes: 4 additions & 3 deletions editoast/editoast_derive/src/modelv2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,8 @@ pub fn model(input: &DeriveInput) -> Result<TokenStream> {
let model_from_row_impl = config.model_from_row_impl();
let changeset_from_model_impl = config.changeset_from_model_impl();

let cs_builder = config.make_builder(true);
let patch_builder = config.make_builder(false);
let changeset_builder = config.changeset_builder_impl_block();
let patch_builder = config.patch_builder_impl_block();

let model_impls = config.make_model_traits_impl();

Expand All @@ -47,8 +47,9 @@ pub fn model(input: &DeriveInput) -> Result<TokenStream> {
#model_from_row_impl
#changeset_from_model_impl

#cs_builder
#changeset_builder
#patch_builder

#model_impls
})
}
Expand Down
77 changes: 20 additions & 57 deletions editoast/editoast_derive/src/modelv2/codegen.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
mod changeset_builder_impl_block;
mod changeset_decl;
mod changeset_from_model;
mod identifiable_impl;
Expand All @@ -8,14 +9,15 @@ mod row_decl;

use proc_macro2::{Span, TokenStream};
use quote::quote;
use syn::parse_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;
Expand Down Expand Up @@ -71,65 +73,26 @@ impl ModelConfig {
}
}

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(parse_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();

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> }
};

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
}

#[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 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(),
}
}

pub(crate) fn patch_builder_impl_block(&self) -> ChangesetBuilderImplBlock {
let mut builder = self.changeset_builder_impl_block();
builder.builder_type = BuilderType::Patch;
builder
}

pub(crate) fn identifiable_impls(&self) -> Vec<IdentifiableImpl> {
self.identifiers
.iter()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
use proc_macro2::Span;
use proc_macro2::TokenStream;
use quote::quote;
use quote::ToTokens;
use syn::parse_quote;

use crate::modelv2::ModelField;

pub(super) enum BuilderType {
Changeset,
Patch,
}

pub(crate) struct ChangesetBuilderImplBlock {
pub(super) builder_type: BuilderType,
pub(super) model: syn::Ident,
pub(super) changeset: syn::Ident,
pub(super) fields: Vec<ModelField>,
}

impl ChangesetBuilderImplBlock {
fn impl_decl(&self) -> TokenStream {
let Self {
model, changeset, ..
} = self;
match self.builder_type {
BuilderType::Changeset => quote! { impl #changeset },
BuilderType::Patch => quote! { impl<'a> crate::modelsv2::Patch<'a, #model> },
}
}

fn builder_field_fn_decl(&self, field: &ModelField) -> syn::ItemFn {
let ident = &field.ident;
let ty = &field.ty;
let value = field.into_transformed(parse_quote! { #ident });
let statement = match self.builder_type {
BuilderType::Changeset => quote! { self.#ident = Some(#value) },
BuilderType::Patch => quote! { self.changeset.#ident = Some(#value) },
};
parse_quote! {
pub fn #ident(mut self, #ident: #ty) -> Self {
#statement;
self
}
}
}

fn builder_flat_fn_decl(&self, field: &ModelField) -> syn::ItemFn {
let ident = &field.ident;
let ty = &field.ty;
let value = field.into_transformed(parse_quote! { #ident });
let statement = match self.builder_type {
BuilderType::Changeset => quote! { self.#ident = #ident.map(|#ident| #value) },
BuilderType::Patch => quote! { self.changeset.#ident = #ident.map(|#ident| #value) },
};
let name = syn::Ident::new(&format!("flat_{}", &field.builder_ident), Span::call_site());
parse_quote! {
pub fn #name(mut self, #ident: Option<#ty>) -> Self {
#statement;
self
}
}
}
}

impl ToTokens for ChangesetBuilderImplBlock {
fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
let impl_decl = self.impl_decl();
let field_fns = self
.fields
.iter()
.map(|field| self.builder_field_fn_decl(field));
let flat_fns = self
.fields
.iter()
.map(|field| self.builder_flat_fn_decl(field));
tokens.extend(quote! {
#[automatically_derived]
#impl_decl {
#(
#[allow(unused)]
#[must_use = "builder methods are intended to be chained"]
#field_fns
)*
#(
#[allow(unused)]
#[must_use = "builder methods are intended to be chained"]
#flat_fns
)*
}
});
}
}

0 comments on commit 6828093

Please sign in to comment.