diff --git a/editoast/editoast_derive/src/modelv2.rs b/editoast/editoast_derive/src/modelv2.rs index b36d9c4a723..1449ba66123 100644 --- a/editoast/editoast_derive/src/modelv2.rs +++ b/editoast/editoast_derive/src/modelv2.rs @@ -19,23 +19,37 @@ pub fn model(input: &DeriveInput) -> Result { 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 }) } diff --git a/editoast/editoast_derive/src/modelv2/args.rs b/editoast/editoast_derive/src/modelv2/args.rs index 667a5551979..f11938834ed 100644 --- a/editoast/editoast_derive/src/modelv2/args.rs +++ b/editoast/editoast_derive/src/modelv2/args.rs @@ -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 + } + } } diff --git a/editoast/editoast_derive/src/modelv2/codegen.rs b/editoast/editoast_derive/src/modelv2/codegen.rs index 93b14c5dbc6..b38c5d3e748 100644 --- a/editoast/editoast_derive/src/modelv2/codegen.rs +++ b/editoast/editoast_derive/src/modelv2/codegen.rs @@ -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 { + 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(), } } diff --git a/editoast/editoast_derive/src/modelv2/codegen/changeset_builder_impl_block.rs b/editoast/editoast_derive/src/modelv2/codegen/changeset_builder_impl_block.rs new file mode 100644 index 00000000000..45855aa27ab --- /dev/null +++ b/editoast/editoast_derive/src/modelv2/codegen/changeset_builder_impl_block.rs @@ -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, +} + +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 + )* + } + }); + } +} diff --git a/editoast/editoast_derive/src/modelv2/codegen/changeset_decl.rs b/editoast/editoast_derive/src/modelv2/codegen/changeset_decl.rs new file mode 100644 index 00000000000..910f0bbfaa1 --- /dev/null +++ b/editoast/editoast_derive/src/modelv2/codegen/changeset_decl.rs @@ -0,0 +1,52 @@ +use super::np; +use quote::quote; +use quote::ToTokens; + +pub(crate) struct ChangesetDecl { + pub(super) vis: syn::Visibility, + pub(super) ident: syn::Ident, + pub(super) table: syn::Path, + pub(super) additional_derives: darling::util::PathList, + pub(super) fields: Vec, +} + +pub(crate) struct ChangesetFieldDecl { + pub(super) vis: syn::Visibility, + pub(super) name: syn::Ident, + pub(super) ty: syn::Type, + pub(super) column: String, +} + +impl ToTokens for ChangesetDecl { + fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) { + let Self { + vis, + ident, + table, + additional_derives, + fields, + } = self; + let np!(field_column, field_vis, field_name, field_type): np!(vec4) = fields + .iter() + .map(|field| { + let ChangesetFieldDecl { + vis, + name, + ty, + column, + } = field; + np!(column, vis, name, ty) + }) + .unzip(); + tokens.extend(quote! { + #[derive(Default, Queryable, AsChangeset, Insertable, #(#additional_derives),*)] + #[diesel(table_name = #table)] + #vis struct #ident { + #( + #[diesel(deserialize_as = #field_type, column_name = #field_column)] + #field_vis #field_name: Option<#field_type> + ),* + } + }); + } +} diff --git a/editoast/editoast_derive/src/modelv2/codegen/changeset_from_model.rs b/editoast/editoast_derive/src/modelv2/codegen/changeset_from_model.rs new file mode 100644 index 00000000000..4c185a722dc --- /dev/null +++ b/editoast/editoast_derive/src/modelv2/codegen/changeset_from_model.rs @@ -0,0 +1,39 @@ +use crate::modelv2::utils::np; +use crate::modelv2::ModelField; +use quote::quote; +use quote::ToTokens; +use syn::parse_quote; + +pub(crate) struct ChangesetFromModelImpl { + pub(super) model: syn::Ident, + pub(super) changeset: syn::Ident, + pub(super) fields: Vec, +} + +impl ToTokens for ChangesetFromModelImpl { + fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) { + let Self { + model, + changeset, + fields, + } = self; + let np!(field, value): np!(vec2) = fields + .iter() + .map(|field| { + let ident = &field.ident; + let expr = field.into_transformed(parse_quote! { model.#ident }); + (ident, expr) + }) + .unzip(); + tokens.extend(quote! { + #[automatically_derived] + impl From<#model> for #changeset { + fn from(model: #model) -> Self { + Self { + #( #field: Some(#value) ),* + } + } + } + }); + } +} diff --git a/editoast/editoast_derive/src/modelv2/codegen/identifiable_impl.rs b/editoast/editoast_derive/src/modelv2/codegen/identifiable_impl.rs new file mode 100644 index 00000000000..6cd880fec44 --- /dev/null +++ b/editoast/editoast_derive/src/modelv2/codegen/identifiable_impl.rs @@ -0,0 +1,22 @@ +use quote::quote; +use quote::ToTokens; + +pub(crate) struct IdentifiableImpl { + pub(super) model: syn::Ident, + pub(super) ty: syn::Type, + pub(super) fields: Vec, +} + +impl ToTokens for IdentifiableImpl { + fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) { + let Self { model, ty, fields } = self; + tokens.extend(quote! { + #[automatically_derived] + impl crate::models::Identifiable<#ty> for #model { + fn get_id(&self) -> #ty { + (#(self.#fields.clone()),*) + } + } + }); + } +} diff --git a/editoast/editoast_derive/src/modelv2/codegen/model_from_row_impl.rs b/editoast/editoast_derive/src/modelv2/codegen/model_from_row_impl.rs new file mode 100644 index 00000000000..b0f4ea8f33e --- /dev/null +++ b/editoast/editoast_derive/src/modelv2/codegen/model_from_row_impl.rs @@ -0,0 +1,35 @@ +use crate::modelv2::utils::np; +use crate::modelv2::ModelField; +use quote::quote; +use quote::ToTokens; +use syn::parse_quote; + +pub(crate) struct ModelFromRowImpl { + pub(super) model: syn::Ident, + pub(super) row: syn::Ident, + pub(super) fields: Vec, +} + +impl ToTokens for ModelFromRowImpl { + fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) { + let Self { model, row, fields } = self; + let np!(field, value): np!(vec2) = fields + .iter() + .map(|field| { + let ident = &field.ident; + let expr = field.from_transformed(parse_quote! { row.#ident }); + (ident, expr) + }) + .unzip(); + tokens.extend(quote! { + #[automatically_derived] + impl From<#row> for #model { + fn from(row: #row) -> Self { + Self { + #( #field: #value ),* + } + } + } + }); + } +} diff --git a/editoast/editoast_derive/src/modelv2/codegen/model_impl.rs b/editoast/editoast_derive/src/modelv2/codegen/model_impl.rs new file mode 100644 index 00000000000..f2827b0676f --- /dev/null +++ b/editoast/editoast_derive/src/modelv2/codegen/model_impl.rs @@ -0,0 +1,24 @@ +use quote::{quote, ToTokens}; + +pub(crate) struct ModelImpl { + pub(super) model: syn::Ident, + pub(super) row: syn::Ident, + pub(super) changeset: syn::Ident, +} + +impl ToTokens for ModelImpl { + fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) { + let Self { + model, + row, + changeset, + } = self; + tokens.extend(quote! { + #[automatically_derived] + impl crate::modelsv2::Model for #model { + type Row = #row; + type Changeset = #changeset; + } + }); + } +} diff --git a/editoast/editoast_derive/src/modelv2/codegen/preferred_id_impl.rs b/editoast/editoast_derive/src/modelv2/codegen/preferred_id_impl.rs new file mode 100644 index 00000000000..c07a8154928 --- /dev/null +++ b/editoast/editoast_derive/src/modelv2/codegen/preferred_id_impl.rs @@ -0,0 +1,17 @@ +use quote::quote; +use quote::ToTokens; + +pub(crate) struct PreferredIdImpl { + pub(super) model: syn::Ident, + pub(super) ty: syn::Type, +} + +impl ToTokens for PreferredIdImpl { + fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) { + let Self { model, ty } = self; + tokens.extend(quote! { + #[automatically_derived] + impl crate::models::PreferredId<#ty> for #model {} + }); + } +} diff --git a/editoast/editoast_derive/src/modelv2/codegen/row_decl.rs b/editoast/editoast_derive/src/modelv2/codegen/row_decl.rs new file mode 100644 index 00000000000..8e37f1cdd06 --- /dev/null +++ b/editoast/editoast_derive/src/modelv2/codegen/row_decl.rs @@ -0,0 +1,49 @@ +use super::np; +use quote::quote; +use quote::ToTokens; + +pub(crate) struct RowDecl { + pub(super) vis: syn::Visibility, + pub(super) ident: syn::Ident, + pub(super) table: syn::Path, + pub(super) additional_derives: darling::util::PathList, + pub(super) fields: Vec, +} + +pub(crate) struct RowFieldDecl { + pub(super) vis: syn::Visibility, + pub(super) name: syn::Ident, + pub(super) ty: syn::Type, + pub(super) column: String, +} + +impl ToTokens for RowDecl { + fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) { + let Self { + vis, + ident, + table, + additional_derives, + fields, + } = self; + let np!(field_column, field_vis, field_name, field_type): np!(vec4) = fields + .iter() + .map(|field| { + let RowFieldDecl { + vis, + name, + ty, + column, + } = field; + np!(column, vis, name, ty) + }) + .unzip(); + tokens.extend(quote! { + #[derive(Queryable, QueryableByName, #(#additional_derives),*)] + #[diesel(table_name = #table)] + #vis struct #ident { + #(#[diesel(column_name = #field_column)] #field_vis #field_name: #field_type),* + } + }); + } +} diff --git a/editoast/editoast_derive/src/modelv2/config.rs b/editoast/editoast_derive/src/modelv2/config.rs index 7ec6dc5e507..3dbce2395c3 100644 --- a/editoast/editoast_derive/src/modelv2/config.rs +++ b/editoast/editoast_derive/src/modelv2/config.rs @@ -3,14 +3,14 @@ use std::{ ops::{Deref, DerefMut}, }; -use proc_macro2::TokenStream; -use quote::quote; +use syn::parse_quote; use super::{args::GeneratedTypeArgs, identifier::Identifier}; #[derive(Debug, PartialEq)] pub struct ModelConfig { pub model: syn::Ident, + pub visibility: syn::Visibility, pub table: syn::Path, pub fields: Fields, pub row: GeneratedTypeArgs, @@ -73,17 +73,14 @@ impl Identifier { } } - pub fn type_expr(&self, config: &ModelConfig) -> TokenStream { + pub fn type_expr(&self, config: &ModelConfig) -> syn::Type { match self { - Self::Field(_) => { - let ty = &self.get_field(config).unwrap().ty; - quote! { #ty } - } + Self::Field(_) => self.get_field(config).unwrap().ty.clone(), Self::Compound(idents) => { let ty = idents .iter() .map(|ident| &config.fields.get(ident).unwrap().ty); - quote! { (#(#ty),*) } + syn::parse_quote! { (#(#ty),*) } // tuple type } } } @@ -113,42 +110,42 @@ impl ModelConfig { impl ModelField { #[allow(clippy::wrong_self_convention)] - pub fn into_transformed(&self, expr: TokenStream) -> TokenStream { + pub fn into_transformed(&self, expr: syn::Expr) -> syn::Expr { match self.transform { - Some(FieldTransformation::Remote(_)) => quote! { #expr.into() }, - Some(FieldTransformation::Json) => quote! { diesel_json::Json(#expr) }, + Some(FieldTransformation::Remote(_)) => parse_quote! { #expr.into() }, + Some(FieldTransformation::Json) => parse_quote! { diesel_json::Json(#expr) }, Some(FieldTransformation::Geo) => unimplemented!("to be designed"), - Some(FieldTransformation::ToString) => quote! { #expr.to_string() }, + Some(FieldTransformation::ToString) => parse_quote! { #expr.to_string() }, Some(FieldTransformation::ToEnum(_)) => { - quote! { #expr as i16 } + parse_quote! { #expr as i16 } } - None => quote! { #expr }, + None => parse_quote! { #expr }, } } #[allow(clippy::wrong_self_convention)] - pub fn from_transformed(&self, expr: TokenStream) -> TokenStream { + pub fn from_transformed(&self, expr: syn::Expr) -> syn::Expr { match self.transform { - Some(FieldTransformation::Remote(_)) => quote! { #expr.into() }, - Some(FieldTransformation::Json) => quote! { #expr.0 }, + Some(FieldTransformation::Remote(_)) => parse_quote! { #expr.into() }, + Some(FieldTransformation::Json) => parse_quote! { #expr.0 }, Some(FieldTransformation::Geo) => unimplemented!("to be designed"), - Some(FieldTransformation::ToString) => quote! { String::from(#expr.parse()) }, + Some(FieldTransformation::ToString) => parse_quote! { String::from(#expr.parse()) }, Some(FieldTransformation::ToEnum(ref ty)) => { - quote! { #ty::from_repr(#expr as usize).expect("Invalid variant repr") } + parse_quote! { #ty::from_repr(#expr as usize).expect("Invalid variant repr") } } - None => quote! { #expr }, + None => parse_quote! { #expr }, } } - pub fn transform_type(&self) -> TokenStream { + pub fn transform_type(&self) -> syn::Type { let ty = &self.ty; match self.transform { - Some(FieldTransformation::Remote(ref ty)) => quote! { #ty }, - Some(FieldTransformation::Json) => quote! { diesel_json::Json<#ty> }, + Some(FieldTransformation::Remote(ref ty)) => parse_quote! { #ty }, + Some(FieldTransformation::Json) => parse_quote! { diesel_json::Json<#ty> }, Some(FieldTransformation::Geo) => unimplemented!("to be designed"), - Some(FieldTransformation::ToString) => quote! { String }, - Some(FieldTransformation::ToEnum(_)) => quote! { i16 }, - None => quote! { #ty }, + Some(FieldTransformation::ToString) => parse_quote! { String }, + Some(FieldTransformation::ToEnum(_)) => parse_quote! { i16 }, + None => ty.clone(), } } } diff --git a/editoast/editoast_derive/src/modelv2/parsing.rs b/editoast/editoast_derive/src/modelv2/parsing.rs index fca7e960010..35789f4acef 100644 --- a/editoast/editoast_derive/src/modelv2/parsing.rs +++ b/editoast/editoast_derive/src/modelv2/parsing.rs @@ -10,7 +10,11 @@ use super::{ }; impl ModelConfig { - pub fn from_macro_args(options: ModelArgs, model_name: syn::Ident) -> darling::Result { + pub fn from_macro_args( + options: ModelArgs, + model_name: syn::Ident, + visibility: syn::Visibility, + ) -> darling::Result { let row = GeneratedTypeArgs { type_name: options.row.type_name.or(Some(format!("{}Row", model_name))), ..options.row @@ -104,6 +108,7 @@ impl ModelConfig { Ok(Self { model: model_name, + visibility, table: options.table, fields, identifiers,