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::HashMap;
use std::sync::Arc;
pub trait FieldFillHandler: Send + Sync {
fn fill(&self, operation: &OperationType) -> Option<AkitaValue>;
fn name(&self) -> &str;
}
pub struct TimestampFillHandler {
format: TimestampFormat,
}
pub enum TimestampFormat {
DateTime,
UnixSeconds,
UnixMillis,
}
impl TimestampFillHandler {
pub fn new() -> Self {
Self {
format: TimestampFormat::DateTime,
}
}
pub fn with_format(mut self, format: TimestampFormat) -> Self {
self.format = format;
self
}
}
impl Default for TimestampFillHandler {
fn default() -> Self {
Self::new()
}
}
impl FieldFillHandler for TimestampFillHandler {
fn fill(&self, _operation: &OperationType) -> Option<AkitaValue> {
let now = chrono::Utc::now();
match self.format {
TimestampFormat::DateTime => Some(AkitaValue::Timestamp(now)),
TimestampFormat::UnixSeconds => Some(AkitaValue::Bigint(now.timestamp())),
TimestampFormat::UnixMillis => Some(AkitaValue::Bigint(now.timestamp_millis())),
}
}
fn name(&self) -> &str {
"TimestampFillHandler"
}
}
pub struct UuidFillHandler;
impl UuidFillHandler {
pub fn new() -> Self {
Self
}
}
impl FieldFillHandler for UuidFillHandler {
fn fill(&self, _operation: &OperationType) -> Option<AkitaValue> {
Some(AkitaValue::Text(uuid::Uuid::new_v4().to_string()))
}
fn name(&self) -> &str {
"UuidFillHandler"
}
}
pub struct FixedValueFillHandler {
value: AkitaValue,
}
impl FixedValueFillHandler {
pub fn new(value: AkitaValue) -> Self {
Self { value }
}
}
impl FieldFillHandler for FixedValueFillHandler {
fn fill(&self, _operation: &OperationType) -> Option<AkitaValue> {
Some(self.value.clone())
}
fn name(&self) -> &str {
"FixedValueFillHandler"
}
}
pub struct FieldFillInterceptor {
handlers: HashMap<String, Box<dyn FieldFillHandler>>,
pub(crate) enabled: bool,
}
impl Default for FieldFillInterceptor {
fn default() -> Self {
Self::new()
}
}
impl FieldFillInterceptor {
pub fn new() -> Self {
Self {
handlers: HashMap::new(),
enabled: true,
}
}
pub fn with_handler(mut self, field_name: &str, handler: Box<dyn FieldFillHandler>) -> Self {
self.handlers.insert(field_name.to_string(), handler);
self
}
pub fn with_enabled(mut self, enabled: bool) -> Self {
self.enabled = enabled;
self
}
pub fn get_fill_values(&self, operation: &OperationType) -> HashMap<String, AkitaValue> {
let mut fills = HashMap::new();
for (field_name, handler) in &self.handlers {
if let Some(value) = handler.fill(operation) {
fills.insert(field_name.clone(), value);
}
}
fills
}
}
impl InterceptorBase for FieldFillInterceptor {
fn name(&self) -> &'static str {
"field_fill"
}
fn interceptor_type(&self) -> InterceptorType {
InterceptorType::FieldFill
}
fn order(&self) -> i32 {
10 }
fn supports_operation(&self, operation: &OperationType) -> bool {
matches!(operation, OperationType::Insert(_) | OperationType::Update)
}
}
#[cfg(any(
feature = "mysql-sync",
feature = "postgres-sync",
feature = "sqlite-sync",
feature = "oracle-sync",
feature = "mssql-sync"
))]
impl crate::interceptor::blocking::AkitaInterceptor for FieldFillInterceptor {
fn before_execute(&self, ctx: &mut ExecuteContext) -> Result<()> {
if !self.enabled {
return Ok(());
}
let operation = ctx.operation_type();
let fill_values = self.get_fill_values(&operation);
if fill_values.is_empty() {
return Ok(());
}
for (field, value) in fill_values {
ctx.set_metadata(format!("fill_{}", field), value.to_string());
}
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
use akita_core::InsertType;
#[test]
fn test_timestamp_fill_handler() {
let handler = TimestampFillHandler::new();
let value = handler.fill(&OperationType::Insert(InsertType::SingleInsert));
assert!(value.is_some(), "Timestamp handler should return a value");
}
#[test]
fn test_uuid_fill_handler() {
let handler = UuidFillHandler::new();
let value = handler.fill(&OperationType::Insert(InsertType::SingleInsert));
assert!(value.is_some(), "UUID handler should return a value");
if let Some(AkitaValue::Text(uuid_str)) = value {
assert!(uuid_str.len() == 36, "UUID should be 36 chars");
}
}
#[test]
fn test_fixed_value_handler() {
let handler = FixedValueFillHandler::new(AkitaValue::Text("test".to_string()));
let value = handler.fill(&OperationType::Insert(InsertType::SingleInsert));
assert_eq!(value, Some(AkitaValue::Text("test".to_string())));
}
#[test]
fn test_field_fill_interceptor() {
let interceptor = FieldFillInterceptor::new()
.with_handler("create_time", Box::new(TimestampFillHandler::new()))
.with_handler("id", Box::new(UuidFillHandler::new()));
let fills = interceptor.get_fill_values(&OperationType::Insert(InsertType::SingleInsert));
assert_eq!(fills.len(), 2, "Should have 2 fill values");
assert!(fills.contains_key("create_time"));
assert!(fills.contains_key("id"));
}
#[test]
fn test_interceptor_base() {
let interceptor = FieldFillInterceptor::new();
assert_eq!(interceptor.name(), "field_fill");
assert_eq!(interceptor.interceptor_type(), InterceptorType::FieldFill);
assert_eq!(interceptor.order(), 10);
assert!(interceptor.supports_operation(&OperationType::Insert(InsertType::SingleInsert)));
assert!(interceptor.supports_operation(&OperationType::Update));
assert!(!interceptor.supports_operation(&OperationType::Select));
assert!(!interceptor.supports_operation(&OperationType::Delete));
}
}