Skip to content

Commit 0f13129

Browse files
committed
editoast: add ToToken ModelV2 definition ChangesetBuilderImplBlock
1 parent ef9250b commit 0f13129

File tree

3 files changed

+117
-60
lines changed

3 files changed

+117
-60
lines changed

editoast/editoast_derive/src/modelv2.rs

+4-3
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,8 @@ pub fn model(input: &DeriveInput) -> Result<TokenStream> {
3131
let model_from_row_impl = config.model_from_row_impl();
3232
let changeset_from_model_impl = config.changeset_from_model_impl();
3333

34-
let cs_builder = config.make_builder(true);
35-
let patch_builder = config.make_builder(false);
34+
let changeset_builder = config.changeset_builder_impl_block();
35+
let patch_builder = config.patch_builder_impl_block();
3636

3737
let model_impls = config.make_model_traits_impl();
3838

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

50-
#cs_builder
50+
#changeset_builder
5151
#patch_builder
52+
5253
#model_impls
5354
})
5455
}

editoast/editoast_derive/src/modelv2/codegen.rs

+20-57
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
mod changeset_builder_impl_block;
12
mod changeset_decl;
23
mod changeset_from_model;
34
mod identifiable_impl;
@@ -8,14 +9,15 @@ mod row_decl;
89

910
use proc_macro2::{Span, TokenStream};
1011
use quote::quote;
11-
use syn::parse_quote;
1212

1313
use crate::modelv2::codegen::changeset_decl::ChangesetDecl;
1414
use crate::modelv2::codegen::changeset_decl::ChangesetFieldDecl;
1515
use crate::modelv2::codegen::model_impl::ModelImpl;
1616
use crate::modelv2::codegen::row_decl::RowDecl;
1717
use crate::modelv2::codegen::row_decl::RowFieldDecl;
1818

19+
use self::changeset_builder_impl_block::BuilderType;
20+
use self::changeset_builder_impl_block::ChangesetBuilderImplBlock;
1921
use self::changeset_from_model::ChangesetFromModelImpl;
2022
use self::identifiable_impl::IdentifiableImpl;
2123
use self::model_from_row_impl::ModelFromRowImpl;
@@ -71,65 +73,26 @@ impl ModelConfig {
7173
}
7274
}
7375

74-
pub fn make_builder(&self, changeset: bool) -> TokenStream {
75-
let np!(fields, fns, flat_fns, types, bodies, flat_bodies): np!(vec6) = self
76-
.iter_fields()
77-
.filter(|f| !self.is_primary(f))
78-
.filter(|field| !field.builder_skip)
79-
.map(|field| {
80-
let ident = &field.ident;
81-
let expr = field.into_transformed(parse_quote! { #ident });
82-
let body = if changeset {
83-
quote! { self.#ident = Some(#expr) }
84-
} else {
85-
quote! { self.changeset.#ident = Some(#expr) }
86-
};
87-
let flat_body = if changeset {
88-
quote! { self.#ident = #ident.map(|#ident| #expr) }
89-
} else {
90-
quote! { self.changeset.#ident = #ident.map(|#ident| #expr) }
91-
};
92-
np!(
93-
ident,
94-
&field.builder_ident,
95-
syn::Ident::new(&format!("flat_{}", &field.builder_ident), Span::call_site()),
96-
&field.ty,
97-
body,
98-
flat_body
99-
)
100-
})
101-
.unzip();
102-
103-
let impl_decl = if changeset {
104-
let tn = self.changeset.ident();
105-
quote! { impl #tn }
106-
} else {
107-
let tn = &self.model;
108-
quote! { impl<'a> crate::modelsv2::Patch<'a, #tn> }
109-
};
110-
111-
quote! {
112-
#[automatically_derived]
113-
#impl_decl {
114-
#(
115-
#[allow(unused)]
116-
#[must_use = "builder methods are intended to be chained"]
117-
pub fn #fns(mut self, #fields: #types) -> Self {
118-
#bodies;
119-
self
120-
}
121-
122-
#[allow(unused)]
123-
#[must_use = "builder methods are intended to be chained"]
124-
pub fn #flat_fns(mut self, #fields: Option<#types>) -> Self {
125-
#flat_bodies;
126-
self
127-
}
128-
)*
129-
}
76+
pub(crate) fn changeset_builder_impl_block(&self) -> ChangesetBuilderImplBlock {
77+
ChangesetBuilderImplBlock {
78+
builder_type: BuilderType::Changeset,
79+
model: self.model.clone(),
80+
changeset: self.changeset.ident(),
81+
fields: self
82+
.iter_fields()
83+
.filter(|field| !self.is_primary(field))
84+
.filter(|field| !field.builder_skip)
85+
.cloned()
86+
.collect(),
13087
}
13188
}
13289

90+
pub(crate) fn patch_builder_impl_block(&self) -> ChangesetBuilderImplBlock {
91+
let mut builder = self.changeset_builder_impl_block();
92+
builder.builder_type = BuilderType::Patch;
93+
builder
94+
}
95+
13396
pub(crate) fn identifiable_impls(&self) -> Vec<IdentifiableImpl> {
13497
self.identifiers
13598
.iter()
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
use proc_macro2::Span;
2+
use proc_macro2::TokenStream;
3+
use quote::quote;
4+
use quote::ToTokens;
5+
use syn::parse_quote;
6+
7+
use crate::modelv2::ModelField;
8+
9+
pub(super) enum BuilderType {
10+
Changeset,
11+
Patch,
12+
}
13+
14+
pub(crate) struct ChangesetBuilderImplBlock {
15+
pub(super) builder_type: BuilderType,
16+
pub(super) model: syn::Ident,
17+
pub(super) changeset: syn::Ident,
18+
pub(super) fields: Vec<ModelField>,
19+
}
20+
21+
impl ChangesetBuilderImplBlock {
22+
fn impl_decl(&self) -> TokenStream {
23+
let Self {
24+
model, changeset, ..
25+
} = self;
26+
match self.builder_type {
27+
BuilderType::Changeset => quote! { impl #changeset },
28+
BuilderType::Patch => quote! { impl<'a> crate::modelsv2::Patch<'a, #model> },
29+
}
30+
}
31+
32+
fn builder_field_fn_decl(&self, field: &ModelField) -> syn::ItemFn {
33+
let ident = &field.ident;
34+
let ty = &field.ty;
35+
let value = field.into_transformed(parse_quote! { #ident });
36+
let statement = match self.builder_type {
37+
BuilderType::Changeset => quote! { self.#ident = Some(#value) },
38+
BuilderType::Patch => quote! { self.changeset.#ident = Some(#value) },
39+
};
40+
parse_quote! {
41+
pub fn #ident(mut self, #ident: #ty) -> Self {
42+
#statement;
43+
self
44+
}
45+
}
46+
}
47+
48+
fn builder_flat_fn_decl(&self, field: &ModelField) -> syn::ItemFn {
49+
let ident = &field.ident;
50+
let ty = &field.ty;
51+
let value = field.into_transformed(parse_quote! { #ident });
52+
let statement = match self.builder_type {
53+
BuilderType::Changeset => quote! { self.#ident = #ident.map(|#ident| #value) },
54+
BuilderType::Patch => quote! { self.changeset.#ident = #ident.map(|#ident| #value) },
55+
};
56+
let name = syn::Ident::new(&format!("flat_{}", &field.builder_ident), Span::call_site());
57+
parse_quote! {
58+
pub fn #name(mut self, #ident: Option<#ty>) -> Self {
59+
#statement;
60+
self
61+
}
62+
}
63+
}
64+
}
65+
66+
impl ToTokens for ChangesetBuilderImplBlock {
67+
fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
68+
let impl_decl = self.impl_decl();
69+
let field_fns = self
70+
.fields
71+
.iter()
72+
.map(|field| self.builder_field_fn_decl(field));
73+
let flat_fns = self
74+
.fields
75+
.iter()
76+
.map(|field| self.builder_flat_fn_decl(field));
77+
tokens.extend(quote! {
78+
#[automatically_derived]
79+
#impl_decl {
80+
#(
81+
#[allow(unused)]
82+
#[must_use = "builder methods are intended to be chained"]
83+
#field_fns
84+
)*
85+
#(
86+
#[allow(unused)]
87+
#[must_use = "builder methods are intended to be chained"]
88+
#flat_fns
89+
)*
90+
}
91+
});
92+
}
93+
}

0 commit comments

Comments
 (0)