From 4f64c668131a949f52b9e30e27dc4c97d0cadf55 Mon Sep 17 00:00:00 2001 From: Ari Seyhun Date: Mon, 11 Oct 2021 14:34:02 +0700 Subject: [PATCH] Add derive `DeriveIntoActiveModel` --- .../src/derives/into_active_model.rs | 101 ++++++++++++++++++ sea-orm-macros/src/derives/mod.rs | 2 + sea-orm-macros/src/lib.rs | 8 ++ src/entity/prelude.rs | 2 +- src/lib.rs | 2 +- 5 files changed, 113 insertions(+), 2 deletions(-) create mode 100644 sea-orm-macros/src/derives/into_active_model.rs diff --git a/sea-orm-macros/src/derives/into_active_model.rs b/sea-orm-macros/src/derives/into_active_model.rs new file mode 100644 index 00000000..d40a0a4b --- /dev/null +++ b/sea-orm-macros/src/derives/into_active_model.rs @@ -0,0 +1,101 @@ +use bae::FromAttributes; +use proc_macro2::{Span, TokenStream}; +use quote::{quote, quote_spanned}; + +#[derive(Default, FromAttributes)] +pub struct SeaOrm { + pub active_model: Option, +} + +enum Error { + InputNotStruct, + Syn(syn::Error), +} + +struct IntoActiveModel { + attrs: SeaOrm, + fields: syn::punctuated::Punctuated, + field_idents: Vec, + ident: syn::Ident, +} + +impl IntoActiveModel { + fn new(input: syn::DeriveInput) -> Result { + let fields = match input.data { + syn::Data::Struct(syn::DataStruct { + fields: syn::Fields::Named(syn::FieldsNamed { named, .. }), + .. + }) => named, + _ => return Err(Error::InputNotStruct), + }; + + let attrs = SeaOrm::try_from_attributes(&input.attrs) + .map_err(Error::Syn)? + .unwrap_or_default(); + + let ident = input.ident; + + let field_idents = fields + .iter() + .map(|field| field.ident.as_ref().unwrap().clone()) + .collect(); + + Ok(IntoActiveModel { + attrs, + fields, + field_idents, + ident, + }) + } + + fn expand(&self) -> syn::Result { + let expanded_impl_into_active_model = self.impl_into_active_model(); + + Ok(expanded_impl_into_active_model) + } + + fn impl_into_active_model(&self) -> TokenStream { + let Self { + attrs, + ident, + field_idents, + fields, + } = self; + + let active_model_ident = attrs + .active_model + .clone() + .unwrap_or_else(|| syn::Ident::new("ActiveModel", Span::call_site())); + + let expanded_fields_into_active_model = fields.iter().map(|field| { + let field_ident = field.ident.as_ref().unwrap(); + + quote!( + ::sea_orm::IntoActiveValue::<_>::into_active_value(self.#field_ident).into() + ) + }); + + quote!( + impl ::sea_orm::IntoActiveModel<#active_model_ident> for #ident { + fn into_active_model(self) -> #active_model_ident { + #active_model_ident { + #( #field_idents: #expanded_fields_into_active_model, )* + ..::std::default::Default::default() + } + } + } + ) + } +} + +pub fn expand_into_active_model(input: syn::DeriveInput) -> syn::Result { + let ident_span = input.ident.span(); + + match IntoActiveModel::new(input) { + Ok(model) => model.expand(), + Err(Error::InputNotStruct) => Ok(quote_spanned! { + ident_span => compile_error!("you can only derive IntoActiveModel on structs"); + }), + Err(Error::Syn(err)) => Err(err), + } +} diff --git a/sea-orm-macros/src/derives/mod.rs b/sea-orm-macros/src/derives/mod.rs index 8dfb7ac1..6ba19a92 100644 --- a/sea-orm-macros/src/derives/mod.rs +++ b/sea-orm-macros/src/derives/mod.rs @@ -4,6 +4,7 @@ mod column; mod entity; mod entity_model; mod from_query_result; +mod into_active_model; mod model; mod primary_key; mod relation; @@ -14,6 +15,7 @@ pub use column::*; pub use entity::*; pub use entity_model::*; pub use from_query_result::*; +pub use into_active_model::*; pub use model::*; pub use primary_key::*; pub use relation::*; diff --git a/sea-orm-macros/src/lib.rs b/sea-orm-macros/src/lib.rs index c3f8a50f..cf8c2f3c 100644 --- a/sea-orm-macros/src/lib.rs +++ b/sea-orm-macros/src/lib.rs @@ -84,6 +84,14 @@ pub fn derive_active_model(input: TokenStream) -> TokenStream { } } +#[proc_macro_derive(DeriveIntoActiveModel, attributes(sea_orm))] +pub fn derive_into_active_model(input: TokenStream) -> TokenStream { + let input = parse_macro_input!(input as DeriveInput); + derives::expand_into_active_model(input) + .unwrap_or_else(Error::into_compile_error) + .into() +} + #[proc_macro_derive(DeriveActiveModelBehavior)] pub fn derive_active_model_behavior(input: TokenStream) -> TokenStream { let DeriveInput { ident, data, .. } = parse_macro_input!(input); diff --git a/src/entity/prelude.rs b/src/entity/prelude.rs index fd61613b..fb6cd822 100644 --- a/src/entity/prelude.rs +++ b/src/entity/prelude.rs @@ -8,7 +8,7 @@ pub use crate::{ #[cfg(feature = "macros")] pub use crate::{ DeriveActiveModel, DeriveActiveModelBehavior, DeriveColumn, DeriveCustomColumn, DeriveEntity, - DeriveEntityModel, DeriveModel, DerivePrimaryKey, DeriveRelation, + DeriveEntityModel, DeriveModel, DerivePrimaryKey, DeriveRelation, DeriveIntoActiveModel }; #[cfg(feature = "with-json")] diff --git a/src/lib.rs b/src/lib.rs index 1b78cf58..c66cb3f2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -287,7 +287,7 @@ pub use schema::*; #[cfg(feature = "macros")] pub use sea_orm_macros::{ DeriveActiveModel, DeriveActiveModelBehavior, DeriveColumn, DeriveCustomColumn, DeriveEntity, - DeriveEntityModel, DeriveModel, DerivePrimaryKey, DeriveRelation, FromQueryResult, + DeriveEntityModel, DeriveModel, DerivePrimaryKey, DeriveRelation, FromQueryResult, DeriveIntoActiveModel, }; pub use sea_query;