Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add derive DeriveIntoActiveModel and IntoActiveValue trait #240

Merged
merged 4 commits into from
Oct 13, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
101 changes: 101 additions & 0 deletions sea-orm-macros/src/derives/into_active_model.rs
Original file line number Diff line number Diff line change
@@ -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),
}
}
2 changes: 2 additions & 0 deletions sea-orm-macros/src/derives/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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::*;
8 changes: 8 additions & 0 deletions sea-orm-macros/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
92 changes: 91 additions & 1 deletion src/entity/active_model.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use crate::{
error::*, ConnectionTrait, DeleteResult, EntityTrait, Iterable, PrimaryKeyToColumn, Value,
};
use async_trait::async_trait;
use sea_query::ValueTuple;
use sea_query::{Nullable, ValueTuple};
use std::fmt::Debug;

#[derive(Clone, Debug, Default)]
Expand Down Expand Up @@ -210,6 +210,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>
where
V: Into<Value>,
Expand Down Expand Up @@ -286,3 +363,16 @@ where
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),
}
}
}
2 changes: 1 addition & 1 deletion src/entity/prelude.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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")]
Expand Down
2 changes: 1 addition & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down