use crate::error::DbError;
use crate::model::Model;
use crate::pool::Connection;
use crate::value::Value;
use std::collections::HashMap;
#[derive(Debug, Clone, PartialEq, Default)]
pub enum ActiveValue<T> {
Set(T),
Unchanged,
#[default]
NotSet,
}
impl<T> ActiveValue<T> {
pub fn is_set(&self) -> bool {
matches!(self, ActiveValue::Set(_))
}
pub fn is_unchanged(&self) -> bool {
matches!(self, ActiveValue::Unchanged)
}
pub fn is_not_set(&self) -> bool {
matches!(self, ActiveValue::NotSet)
}
pub fn into_value(self) -> Option<T> {
match self {
ActiveValue::Set(v) => Some(v),
_ => None,
}
}
pub fn as_value(&self) -> Option<&T> {
match self {
ActiveValue::Set(v) => Some(v),
_ => None,
}
}
}
impl<T: Into<Value>> From<T> for ActiveValue<Value> {
fn from(value: T) -> Self {
ActiveValue::Set(value.into())
}
}
pub trait ActiveModelTrait: Send + Sync {
fn table_name(&self) -> &str;
fn pk_value(&self) -> Option<Value>;
fn for_each_changed<F>(&self, f: F)
where
F: FnMut(&str, &ActiveValue<Value>);
}
#[derive(Debug, Clone)]
pub struct ActiveModel<M: Model> {
model: M,
changes: HashMap<String, ActiveValue<Value>>,
}
impl<M: Model> ActiveModel<M> {
pub fn from_model(model: M) -> Self {
Self {
model,
changes: HashMap::new(),
}
}
pub fn set(&mut self, field: impl Into<String>, value: ActiveValue<Value>) {
self.changes.insert(field.into(), value);
}
pub fn unset(&mut self, field: &str) {
self.changes.remove(field);
}
pub fn get(&self, field: &str) -> Option<&ActiveValue<Value>> {
self.changes.get(field)
}
pub fn changed_fields(&self) -> Vec<(&str, &Value)> {
self.changes
.iter()
.filter_map(|(k, v)| match v {
ActiveValue::Set(val) => Some((k.as_str(), val)),
_ => None,
})
.collect()
}
pub fn as_mut_model(&mut self) -> &mut M {
&mut self.model
}
pub fn as_model(&self) -> &M {
&self.model
}
pub fn into_model(self) -> M {
self.model
}
}
impl<M: Model> ActiveModelTrait for ActiveModel<M>
where
M::PrimaryKey: Into<Value>,
{
fn table_name(&self) -> &str {
M::table_name()
}
fn pk_value(&self) -> Option<Value> {
let v = self.model.pk_as_value();
if v.is_null() {
None
} else {
Some(v)
}
}
fn for_each_changed<F>(&self, mut f: F)
where
F: FnMut(&str, &ActiveValue<Value>),
{
for (key, av) in self.changes.iter() {
f(key, av);
}
}
}
pub async fn update<A, C>(conn: &mut C, active: A) -> Result<u64, DbError>
where
A: ActiveModelTrait,
C: Connection + ?Sized,
{
let table = active.table_name().to_string();
let pk_value = active
.pk_value()
.ok_or_else(|| DbError::QueryError("ActiveModel: primary key is not set".to_string()))?;
let mut set_clauses: Vec<String> = Vec::new();
let mut params: Vec<Value> = Vec::new();
active.for_each_changed(|field, av| {
if let ActiveValue::Set(val) = av {
set_clauses.push(format!("{} = {}", field, val.to_param()));
params.push(val.clone());
}
});
if set_clauses.is_empty() {
return Ok(0);
}
let sql = format!(
"UPDATE {} SET {} WHERE {} = {}",
table,
set_clauses.join(", "),
active.pk_name_for_update(),
pk_value.to_param()
);
conn.execute(&sql).await
}
pub async fn save<A, C>(conn: &mut C, active: A) -> Result<u64, DbError>
where
A: ActiveModelTrait,
C: Connection + ?Sized,
{
if active.pk_value().is_some() {
update(conn, active).await
} else {
insert(conn, active).await
}
}
async fn insert<A, C>(conn: &mut C, active: A) -> Result<u64, DbError>
where
A: ActiveModelTrait,
C: Connection + ?Sized,
{
let table = active.table_name().to_string();
let mut columns: Vec<String> = Vec::new();
let mut values: Vec<String> = Vec::new();
active.for_each_changed(|field, av| {
if let ActiveValue::Set(val) = av {
columns.push(field.to_string());
values.push(val.to_param().into_owned());
}
});
if columns.is_empty() {
return Err(DbError::QueryError(
"ActiveModel: no fields set for insert".to_string(),
));
}
let sql = format!(
"INSERT INTO {} ({}) VALUES ({})",
table,
columns.join(", "),
values.join(", ")
);
conn.execute(&sql).await
}
pub trait ActiveModelExt: ActiveModelTrait {
fn pk_name_for_update(&self) -> &str {
"id"
}
}
impl<A: ActiveModelTrait> ActiveModelExt for A {}
#[cfg(test)]
mod tests {
use super::*;
#[derive(Debug, Clone, Default)]
#[allow(dead_code)]
struct User {
id: i64,
name: String,
email: String,
}
impl Model for User {
type PrimaryKey = i64;
fn table_name() -> &'static str {
"users"
}
fn pk_name() -> &'static str {
"id"
}
fn pk(&self) -> Self::PrimaryKey {
self.id
}
fn set_pk(&mut self, pk: Self::PrimaryKey) {
self.id = pk;
}
fn pk_as_value(&self) -> Value {
Value::I64(self.id)
}
}
#[test]
fn test_active_value_set() {
let av: ActiveValue<Value> = ActiveValue::Set(Value::String("Alice".into()));
assert!(av.is_set());
assert!(!av.is_unchanged());
assert!(!av.is_not_set());
assert_eq!(av.into_value(), Some(Value::String("Alice".into())));
}
#[test]
fn test_active_value_unchanged() {
let av: ActiveValue<Value> = ActiveValue::Unchanged;
assert!(!av.is_set());
assert!(av.is_unchanged());
assert!(!av.is_not_set());
assert_eq!(av.into_value(), None);
}
#[test]
fn test_active_value_not_set() {
let av: ActiveValue<Value> = ActiveValue::NotSet;
assert!(!av.is_set());
assert!(!av.is_unchanged());
assert!(av.is_not_set());
assert_eq!(av.into_value(), None);
}
#[test]
fn test_active_value_default_is_not_set() {
let av: ActiveValue<Value> = ActiveValue::default();
assert!(av.is_not_set());
}
#[test]
fn test_active_value_from_str() {
let av: ActiveValue<Value> = "hello".into();
assert!(av.is_set());
assert_eq!(av.into_value(), Some(Value::String("hello".into())));
}
#[test]
fn test_active_value_from_i64() {
let av: ActiveValue<Value> = 42i64.into();
assert!(av.is_set());
assert_eq!(av.into_value(), Some(Value::I64(42)));
}
#[test]
fn test_active_value_as_value() {
let av = ActiveValue::Set(Value::I64(99));
assert_eq!(av.as_value(), Some(&Value::I64(99)));
let unchanged: ActiveValue<Value> = ActiveValue::Unchanged;
assert_eq!(unchanged.as_value(), None);
}
#[test]
fn test_active_model_from_model() {
let user = User {
id: 1,
name: "Alice".into(),
email: "alice@example.com".into(),
};
let active = ActiveModel::from_model(user.clone());
assert_eq!(active.table_name(), "users");
assert_eq!(active.pk_value(), Some(Value::I64(1)));
assert!(active.changed_fields().is_empty());
}
#[test]
fn test_active_model_set_and_changed_fields() {
let user = User {
id: 1,
name: "Alice".into(),
email: "alice@example.com".into(),
};
let mut active = ActiveModel::from_model(user);
active.set(
"email",
ActiveValue::Set(Value::String("new@example.com".into())),
);
active.set("name", ActiveValue::Unchanged);
let changed = active.changed_fields();
assert_eq!(changed.len(), 1);
assert_eq!(changed[0].0, "email");
assert_eq!(changed[0].1, &Value::String("new@example.com".into()));
}
#[test]
fn test_active_model_for_each_changed() {
let user = User {
id: 1,
name: "Alice".into(),
email: "alice@example.com".into(),
};
let mut active = ActiveModel::from_model(user);
active.set("name", ActiveValue::Set(Value::String("Bob".into())));
active.set(
"email",
ActiveValue::Set(Value::String("bob@example.com".into())),
);
active.set("extra", ActiveValue::NotSet);
let mut count = 0;
let mut names: Vec<String> = Vec::new();
active.for_each_changed(|field, av| {
count += 1;
names.push(field.to_string());
let _ = av;
});
assert_eq!(count, 3); assert!(names.contains(&"name".to_string()));
assert!(names.contains(&"email".to_string()));
assert!(names.contains(&"extra".to_string()));
}
#[test]
fn test_active_model_unset() {
let user = User::default();
let mut active = ActiveModel::from_model(user);
active.set("name", ActiveValue::Set(Value::String("Alice".into())));
assert_eq!(active.changed_fields().len(), 1);
active.unset("name");
assert!(active.changed_fields().is_empty());
}
#[test]
fn test_active_model_get() {
let user = User::default();
let mut active = ActiveModel::from_model(user);
active.set("name", ActiveValue::Set(Value::String("Alice".into())));
assert!(active.get("name").is_some());
assert!(active.get("email").is_none());
}
#[test]
fn test_active_model_into_model() {
let user = User {
id: 42,
name: "Original".into(),
email: "orig@example.com".into(),
};
let active = ActiveModel::from_model(user.clone());
let restored = active.into_model();
assert_eq!(restored.id, user.id);
assert_eq!(restored.name, user.name);
}
#[test]
fn test_active_model_as_mut_model() {
let user = User::default();
let mut active = ActiveModel::from_model(user);
active.as_mut_model().name = "Modified".into();
assert_eq!(active.as_model().name, "Modified");
}
#[test]
fn test_three_state_semantics() {
let user = User {
id: 1,
name: "Alice".into(),
email: "alice@example.com".into(),
};
let mut active = ActiveModel::from_model(user);
active.set(
"email",
ActiveValue::Set(Value::String("new@example.com".into())),
);
let changed = active.changed_fields();
assert_eq!(changed.len(), 1);
assert_eq!(changed[0].0, "email");
}
#[test]
fn test_new_record_all_not_set() {
let user = User::default();
let mut active = ActiveModel::from_model(user);
assert!(active.changed_fields().is_empty());
active.set("name", ActiveValue::Set(Value::String("Bob".into())));
active.set(
"email",
ActiveValue::Set(Value::String("bob@example.com".into())),
);
let changed = active.changed_fields();
assert_eq!(changed.len(), 2);
}
#[test]
fn test_active_value_clone_and_debug() {
let av = ActiveValue::Set(Value::I64(100));
let av2 = av.clone();
assert_eq!(av, av2);
let debug_str = format!("{:?}", av);
assert!(debug_str.contains("Set"));
}
}