Merge pull request #240 from Acidic9/feat/into-active-model-derive
Add derive `DeriveIntoActiveModel` and `IntoActiveValue` trait
This commit is contained in:
commit
d5e95b0f8f
101
sea-orm-macros/src/derives/into_active_model.rs
Normal file
101
sea-orm-macros/src/derives/into_active_model.rs
Normal 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),
|
||||||
|
}
|
||||||
|
}
|
@ -4,6 +4,7 @@ mod column;
|
|||||||
mod entity;
|
mod entity;
|
||||||
mod entity_model;
|
mod entity_model;
|
||||||
mod from_query_result;
|
mod from_query_result;
|
||||||
|
mod into_active_model;
|
||||||
mod model;
|
mod model;
|
||||||
mod primary_key;
|
mod primary_key;
|
||||||
mod relation;
|
mod relation;
|
||||||
@ -14,6 +15,7 @@ pub use column::*;
|
|||||||
pub use entity::*;
|
pub use entity::*;
|
||||||
pub use entity_model::*;
|
pub use entity_model::*;
|
||||||
pub use from_query_result::*;
|
pub use from_query_result::*;
|
||||||
|
pub use into_active_model::*;
|
||||||
pub use model::*;
|
pub use model::*;
|
||||||
pub use primary_key::*;
|
pub use primary_key::*;
|
||||||
pub use relation::*;
|
pub use relation::*;
|
||||||
|
@ -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)]
|
#[proc_macro_derive(DeriveActiveModelBehavior)]
|
||||||
pub fn derive_active_model_behavior(input: TokenStream) -> TokenStream {
|
pub fn derive_active_model_behavior(input: TokenStream) -> TokenStream {
|
||||||
let DeriveInput { ident, data, .. } = parse_macro_input!(input);
|
let DeriveInput { ident, data, .. } = parse_macro_input!(input);
|
||||||
|
@ -2,7 +2,7 @@ use crate::{
|
|||||||
error::*, ConnectionTrait, DeleteResult, EntityTrait, Iterable, PrimaryKeyToColumn, Value,
|
error::*, ConnectionTrait, DeleteResult, EntityTrait, Iterable, PrimaryKeyToColumn, Value,
|
||||||
};
|
};
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use sea_query::ValueTuple;
|
use sea_query::{Nullable, ValueTuple};
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
|
|
||||||
#[derive(Clone, Debug, Default)]
|
#[derive(Clone, Debug, Default)]
|
||||||
@ -214,6 +214,83 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub trait IntoActiveValue<V>
|
||||||
|
where
|
||||||
|
V: Into<Value>,
|
||||||
|
{
|
||||||
|
fn into_active_value(self) -> ActiveValue<V>;
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! impl_into_active_value {
|
||||||
|
($ty: ty, $fn: ident) => {
|
||||||
|
impl IntoActiveValue<$ty> for $ty {
|
||||||
|
fn into_active_value(self) -> ActiveValue<$ty> {
|
||||||
|
$fn(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IntoActiveValue<Option<$ty>> for Option<$ty> {
|
||||||
|
fn into_active_value(self) -> ActiveValue<Option<$ty>> {
|
||||||
|
match self {
|
||||||
|
Some(value) => Set(Some(value)),
|
||||||
|
None => Unset(None),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IntoActiveValue<Option<$ty>> for Option<Option<$ty>> {
|
||||||
|
fn into_active_value(self) -> ActiveValue<Option<$ty>> {
|
||||||
|
match self {
|
||||||
|
Some(value) => Set(value),
|
||||||
|
None => Unset(None),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
impl_into_active_value!(bool, Set);
|
||||||
|
impl_into_active_value!(i8, Set);
|
||||||
|
impl_into_active_value!(i16, Set);
|
||||||
|
impl_into_active_value!(i32, Set);
|
||||||
|
impl_into_active_value!(i64, Set);
|
||||||
|
impl_into_active_value!(u8, Set);
|
||||||
|
impl_into_active_value!(u16, Set);
|
||||||
|
impl_into_active_value!(u32, Set);
|
||||||
|
impl_into_active_value!(u64, Set);
|
||||||
|
impl_into_active_value!(f32, Set);
|
||||||
|
impl_into_active_value!(f64, Set);
|
||||||
|
impl_into_active_value!(&'static str, Set);
|
||||||
|
impl_into_active_value!(String, Set);
|
||||||
|
|
||||||
|
#[cfg(feature = "with-json")]
|
||||||
|
#[cfg_attr(docsrs, doc(cfg(feature = "with-json")))]
|
||||||
|
impl_into_active_value!(crate::prelude::Json, Set);
|
||||||
|
|
||||||
|
#[cfg(feature = "with-chrono")]
|
||||||
|
#[cfg_attr(docsrs, doc(cfg(feature = "with-chrono")))]
|
||||||
|
impl_into_active_value!(crate::prelude::Date, Set);
|
||||||
|
|
||||||
|
#[cfg(feature = "with-chrono")]
|
||||||
|
#[cfg_attr(docsrs, doc(cfg(feature = "with-chrono")))]
|
||||||
|
impl_into_active_value!(crate::prelude::Time, Set);
|
||||||
|
|
||||||
|
#[cfg(feature = "with-chrono")]
|
||||||
|
#[cfg_attr(docsrs, doc(cfg(feature = "with-chrono")))]
|
||||||
|
impl_into_active_value!(crate::prelude::DateTime, Set);
|
||||||
|
|
||||||
|
#[cfg(feature = "with-chrono")]
|
||||||
|
#[cfg_attr(docsrs, doc(cfg(feature = "with-chrono")))]
|
||||||
|
impl_into_active_value!(crate::prelude::DateTimeWithTimeZone, Set);
|
||||||
|
|
||||||
|
#[cfg(feature = "with-rust_decimal")]
|
||||||
|
#[cfg_attr(docsrs, doc(cfg(feature = "with-rust_decimal")))]
|
||||||
|
impl_into_active_value!(crate::prelude::Decimal, Set);
|
||||||
|
|
||||||
|
#[cfg(feature = "with-uuid")]
|
||||||
|
#[cfg_attr(docsrs, doc(cfg(feature = "with-uuid")))]
|
||||||
|
impl_into_active_value!(crate::prelude::Uuid, Set);
|
||||||
|
|
||||||
impl<V> ActiveValue<V>
|
impl<V> ActiveValue<V>
|
||||||
where
|
where
|
||||||
V: Into<Value>,
|
V: Into<Value>,
|
||||||
@ -290,3 +367,16 @@ where
|
|||||||
self.value.as_ref() == other.value.as_ref()
|
self.value.as_ref() == other.value.as_ref()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<V> From<ActiveValue<V>> for ActiveValue<Option<V>>
|
||||||
|
where
|
||||||
|
V: Into<Value> + Nullable,
|
||||||
|
{
|
||||||
|
fn from(value: ActiveValue<V>) -> Self {
|
||||||
|
match value.state {
|
||||||
|
ActiveValueState::Set => Set(value.value),
|
||||||
|
ActiveValueState::Unset => Unset(None),
|
||||||
|
ActiveValueState::Unchanged => ActiveValue::unchanged(value.value),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -8,7 +8,7 @@ pub use crate::{
|
|||||||
#[cfg(feature = "macros")]
|
#[cfg(feature = "macros")]
|
||||||
pub use crate::{
|
pub use crate::{
|
||||||
DeriveActiveModel, DeriveActiveModelBehavior, DeriveColumn, DeriveCustomColumn, DeriveEntity,
|
DeriveActiveModel, DeriveActiveModelBehavior, DeriveColumn, DeriveCustomColumn, DeriveEntity,
|
||||||
DeriveEntityModel, DeriveModel, DerivePrimaryKey, DeriveRelation,
|
DeriveEntityModel, DeriveModel, DerivePrimaryKey, DeriveRelation, DeriveIntoActiveModel
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(feature = "with-json")]
|
#[cfg(feature = "with-json")]
|
||||||
|
@ -287,7 +287,7 @@ pub use schema::*;
|
|||||||
#[cfg(feature = "macros")]
|
#[cfg(feature = "macros")]
|
||||||
pub use sea_orm_macros::{
|
pub use sea_orm_macros::{
|
||||||
DeriveActiveModel, DeriveActiveModelBehavior, DeriveColumn, DeriveCustomColumn, DeriveEntity,
|
DeriveActiveModel, DeriveActiveModelBehavior, DeriveColumn, DeriveCustomColumn, DeriveEntity,
|
||||||
DeriveEntityModel, DeriveModel, DerivePrimaryKey, DeriveRelation, FromQueryResult,
|
DeriveEntityModel, DeriveModel, DerivePrimaryKey, DeriveRelation, FromQueryResult, DeriveIntoActiveModel,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub use sea_query;
|
pub use sea_query;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user