use crate::{ error::*, DatabaseConnection, DeleteResult, EntityTrait, Iterable, PrimaryKeyToColumn, PrimaryKeyTrait, Value, }; use std::fmt::Debug; #[derive(Clone, Debug, Default)] pub struct ActiveValue where V: Into + Default, { value: V, state: ActiveValueState, } #[allow(non_snake_case)] pub fn Set(v: V) -> ActiveValue where V: Into + Default, { ActiveValue::set(v) } #[allow(non_snake_case)] pub fn Unset(_: Option) -> ActiveValue where V: Into + Default, { 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 + Default, { ActiveValue::unchanged(value) } 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; // below is not yet possible. right now we define these methods in DeriveActiveModel // fn save(self, db: &DatabaseConnection) -> impl Future>; // fn delete(self, db: &DatabaseConnection) -> impl Future>; } /// Behaviors for users to override 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) -> Self { self } /// Will be called after saving fn after_save(self) -> Self { self } /// Will be called before deleting fn before_delete(self) -> Self { 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 + Default, { pub fn set(value: V) -> Self { Self { value, state: ActiveValueState::Set, } } pub fn is_set(&self) -> bool { matches!(self.state, ActiveValueState::Set) } pub(crate) fn unchanged(value: V) -> Self { Self { value, state: ActiveValueState::Unchanged, } } pub fn is_unchanged(&self) -> bool { matches!(self.state, ActiveValueState::Unchanged) } pub fn unset() -> Self { Self { value: V::default(), state: ActiveValueState::Unset, } } pub fn is_unset(&self) -> bool { matches!(self.state, ActiveValueState::Unset) } pub fn take(&mut self) -> V { self.state = ActiveValueState::Unset; std::mem::take(&mut self.value) } pub fn unwrap(self) -> V { self.value } pub fn into_value(self) -> Value { self.value.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 + Default, { fn as_ref(&self) -> &V { &self.value } } impl PartialEq for ActiveValue where V: Into + Default + std::cmp::PartialEq, { fn eq(&self, other: &Self) -> bool { self.value == other.value } } /// Insert the model if primary key is unset, update otherwise. /// Only works if the entity has auto increment primary key. pub async fn save_active_model(mut am: A, db: &DatabaseConnection) -> Result where A: ActiveModelBehavior + ActiveModelTrait, E::Model: IntoActiveModel, E: EntityTrait, { am = ActiveModelBehavior::before_save(am); let mut is_update = true; for key in E::PrimaryKey::iter() { let col = key.into_column(); if am.is_unset(col) { is_update = false; break; } } if !is_update { am = insert_and_select_active_model::(am, db).await?; } else { am = update_active_model::(am, db).await?; } am = ActiveModelBehavior::after_save(am); Ok(am) } async fn insert_and_select_active_model(am: A, db: &DatabaseConnection) -> Result where A: ActiveModelTrait, E::Model: IntoActiveModel, E: EntityTrait, { let exec = E::insert(am).exec(db); let res = exec.await?; // TODO: if the entity does not have auto increment primary key, then last_insert_id is a wrong value if ::auto_increment() && res.last_insert_id != 0 { let find = E::find_by_id(res.last_insert_id).one(db); let res = find.await; let model: Option = res?; match model { Some(model) => Ok(model.into_active_model()), None => Err(SeaErr::Execution), } } else { Ok(A::default()) } } async fn update_active_model(am: A, db: &DatabaseConnection) -> Result where A: ActiveModelTrait, E: EntityTrait, { let exec = E::update(am).exec(db); exec.await } pub async fn delete_active_model( mut am: A, db: &DatabaseConnection, ) -> Result where A: ActiveModelBehavior + ActiveModelTrait, E: EntityTrait, { am = ActiveModelBehavior::before_delete(am); let exec = E::delete(am).exec(db); exec.await }