use crate::{ error::*, DatabaseConnection, DeleteResult, EntityTrait, Iterable, PrimaryKeyToColumn, PrimaryKeyTrait, Value, }; use async_trait::async_trait; use std::fmt::Debug; #[derive(Clone, Debug, Default)] pub struct ActiveValue where V: Into, { value: Option, state: ActiveValueState, } #[allow(non_snake_case)] pub fn Set(v: V) -> ActiveValue where V: Into, { ActiveValue::set(v) } #[allow(non_snake_case)] pub fn Unset(_: Option) -> ActiveValue where V: Into, { ActiveValue::unset() } #[derive(Clone, Debug)] enum ActiveValueState { Set, Unchanged, Unset, } impl Default for ActiveValueState { fn default() -> Self { Self::Unset } } #[doc(hidden)] pub fn unchanged_active_value_not_intended_for_public_use(value: V) -> ActiveValue where V: Into, { ActiveValue::unchanged(value) } #[async_trait] pub trait ActiveModelTrait: Clone + Debug { type Entity: EntityTrait; fn take(&mut self, c: ::Column) -> ActiveValue; fn get(&self, c: ::Column) -> ActiveValue; fn set(&mut self, c: ::Column, v: Value); fn unset(&mut self, c: ::Column); fn is_unset(&self, c: ::Column) -> bool; fn default() -> Self; async fn insert(self, db: &DatabaseConnection) -> Result where Self: ActiveModelBehavior, ::Model: IntoActiveModel, { let am = ActiveModelBehavior::before_save(self, true, db)?; let res = ::insert(am).exec(db).await?; // Assume valid last_insert_id is not equals to Default::default() if res.last_insert_id != <::PrimaryKey as PrimaryKeyTrait>::ValueType::default() { let found = ::find_by_id(res.last_insert_id) .one(db) .await?; let am = match found { Some(model) => Ok(model.into_active_model()), None => Err(DbErr::Exec("Failed to find inserted item".to_owned())), }?; ActiveModelBehavior::after_save(am, true, db) } else { Ok(Self::default()) } } async fn update(self, db: &DatabaseConnection) -> Result where Self: ActiveModelBehavior, { let am = ActiveModelBehavior::before_save(self, false, db)?; let am = Self::Entity::update(am).exec(db).await?; ActiveModelBehavior::after_save(am, false, db) } /// Insert the model if primary key is unset, update otherwise. /// Only works if the entity has auto increment primary key. async fn save(self, db: &DatabaseConnection) -> Result where Self: ActiveModelBehavior, ::Model: IntoActiveModel, { let mut am = self; let mut is_update = true; for key in ::PrimaryKey::iter() { let col = key.into_column(); if am.is_unset(col) { is_update = false; break; } } if !is_update { am = am.insert(db).await?; } else { am = am.update(db).await?; } Ok(am) } /// Delete an active model by its primary key async fn delete(self, db: &DatabaseConnection) -> Result where Self: ActiveModelBehavior, { let am = ActiveModelBehavior::before_delete(self, db)?; let am_clone = am.clone(); let delete_res = Self::Entity::delete(am).exec(db).await?; ActiveModelBehavior::after_delete(am_clone, db)?; Ok(delete_res) } } /// Behaviors for users to override #[allow(unused_variables)] pub trait ActiveModelBehavior: ActiveModelTrait { /// Create a new ActiveModel with default values. Also used by `Default::default()`. fn new() -> Self { ::default() } /// Will be called before saving fn before_save(self, insert: bool, db: &DatabaseConnection) -> Result { Ok(self) } /// Will be called after saving fn after_save(self, insert: bool, db: &DatabaseConnection) -> Result { Ok(self) } /// Will be called before deleting fn before_delete(self, db: &DatabaseConnection) -> Result { Ok(self) } /// Will be called after deleting fn after_delete(self, db: &DatabaseConnection) -> Result { Ok(self) } } pub trait IntoActiveModel where A: ActiveModelTrait, { fn into_active_model(self) -> A; } impl IntoActiveModel for A where A: ActiveModelTrait, { fn into_active_model(self) -> A { self } } impl ActiveValue where V: Into, { pub fn set(value: V) -> Self { Self { value: Some(value), state: ActiveValueState::Set, } } pub fn is_set(&self) -> bool { matches!(self.state, ActiveValueState::Set) } pub(crate) fn unchanged(value: V) -> Self { Self { value: Some(value), state: ActiveValueState::Unchanged, } } pub fn is_unchanged(&self) -> bool { matches!(self.state, ActiveValueState::Unchanged) } pub fn unset() -> Self { Self { value: None, state: ActiveValueState::Unset, } } pub fn is_unset(&self) -> bool { matches!(self.state, ActiveValueState::Unset) } pub fn take(&mut self) -> V { self.state = ActiveValueState::Unset; self.value.take().unwrap() } pub fn unwrap(self) -> V { self.value.unwrap() } pub fn into_value(self) -> Value { self.value.unwrap().into() } pub fn into_wrapped_value(self) -> ActiveValue { match self.state { ActiveValueState::Set => ActiveValue::set(self.into_value()), ActiveValueState::Unchanged => ActiveValue::unchanged(self.into_value()), ActiveValueState::Unset => ActiveValue::unset(), } } } impl std::convert::AsRef for ActiveValue where V: Into, { fn as_ref(&self) -> &V { self.value.as_ref().unwrap() } } impl PartialEq for ActiveValue where V: Into + std::cmp::PartialEq, { fn eq(&self, other: &Self) -> bool { self.value.as_ref() == other.value.as_ref() } }