Add derive DeriveIntoActiveModel

This commit is contained in:
Ari Seyhun 2021-10-11 14:34:02 +07:00
parent 8667b2ddfc
commit 4f64c66813
5 changed files with 113 additions and 2 deletions

View File

@ -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<syn::Ident>,
}
enum Error {
InputNotStruct,
Syn(syn::Error),
}
struct IntoActiveModel {
attrs: SeaOrm,
fields: syn::punctuated::Punctuated<syn::Field, syn::token::Comma>,
field_idents: Vec<syn::Ident>,
ident: syn::Ident,
}
impl IntoActiveModel {
fn new(input: syn::DeriveInput) -> Result<Self, Error> {
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<TokenStream> {
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<TokenStream> {
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),
}
}

View File

@ -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::*;

View File

@ -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);

View File

@ -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")]

View File

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