use crate::comm::ExecuteContext;
use crate::errors::Result;
use crate::interceptor::shared::InterceptorBase;
use crate::interceptor::{InterceptorType, OperationType};
use akita_core::AkitaValue;
use std::collections::HashSet;
#[derive(Debug, Clone)]
pub struct SoftDeleteConfig {
pub column: String,
pub deleted_value: AkitaValue,
pub not_deleted_value: AkitaValue,
pub ignore_tables: HashSet<String>,
}
impl Default for SoftDeleteConfig {
fn default() -> Self {
Self {
column: "deleted".to_string(),
deleted_value: AkitaValue::Int(1),
not_deleted_value: AkitaValue::Int(0),
ignore_tables: HashSet::new(),
}
}
}
impl SoftDeleteConfig {
pub fn with_column(mut self, column: &str) -> Self {
self.column = column.to_string();
self
}
pub fn ignore_table(mut self, table: &str) -> Self {
self.ignore_tables.insert(table.to_string());
self
}
}
pub struct SoftDeleteInterceptor {
config: SoftDeleteConfig,
}
impl SoftDeleteInterceptor {
pub fn new(config: SoftDeleteConfig) -> Self {
Self { config }
}
pub fn with_default() -> Self {
Self::new(SoftDeleteConfig::default())
}
pub fn config(&self) -> &SoftDeleteConfig {
&self.config
}
pub(crate) fn should_ignore_table(&self, table: &str) -> bool {
self.config.ignore_tables.contains(table)
}
}
impl InterceptorBase for SoftDeleteInterceptor {
fn name(&self) -> &'static str {
"soft_delete"
}
fn interceptor_type(&self) -> InterceptorType {
InterceptorType::SoftDelete
}
fn order(&self) -> i32 {
20 }
fn will_ignore_table(&self, table_name: &str) -> bool {
self.should_ignore_table(table_name)
}
}
#[cfg(any(
feature = "mysql-sync",
feature = "postgres-sync",
feature = "sqlite-sync",
feature = "oracle-sync",
feature = "mssql-sync"
))]
impl crate::interceptor::blocking::AkitaInterceptor for SoftDeleteInterceptor {
fn before_execute(&self, ctx: &mut ExecuteContext) -> Result<()> {
let table = ctx.table_info().name.clone();
if self.should_ignore_table(&table) {
return Ok(());
}
match ctx.operation_type() {
OperationType::Delete => {
ctx.set_metadata("soft_delete_rewrite".to_string(), "true".to_string());
ctx.set_metadata("soft_delete_column".to_string(), self.config.column.clone());
}
OperationType::Select => {
ctx.set_metadata("soft_delete_filter".to_string(), "true".to_string());
ctx.set_metadata("soft_delete_column".to_string(), self.config.column.clone());
}
_ => {}
}
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_soft_delete_config_default() {
let config = SoftDeleteConfig::default();
assert_eq!(config.column, "deleted");
assert_eq!(config.deleted_value, AkitaValue::Int(1));
assert_eq!(config.not_deleted_value, AkitaValue::Int(0));
}
#[test]
fn test_soft_delete_config_custom() {
let config = SoftDeleteConfig::default()
.with_column("is_deleted")
.ignore_table("logs");
assert_eq!(config.column, "is_deleted");
assert!(config.ignore_tables.contains("logs"));
}
#[test]
fn test_soft_delete_interceptor() {
let interceptor = SoftDeleteInterceptor::with_default();
assert_eq!(interceptor.name(), "soft_delete");
assert_eq!(interceptor.interceptor_type(), InterceptorType::SoftDelete);
assert_eq!(interceptor.order(), 20);
}
#[test]
fn test_should_ignore_table() {
let interceptor =
SoftDeleteInterceptor::new(SoftDeleteConfig::default().ignore_table("logs"));
assert!(interceptor.should_ignore_table("logs"));
assert!(!interceptor.should_ignore_table("users"));
}
}