pub use cloacina_workflow::Context;
use crate::error::ContextError;
use crate::models::context::{DbContext, NewDbContext};
use crate::{UniversalTimestamp, UniversalUuid};
use serde::{Deserialize, Serialize};
use std::fmt::Debug;
use tracing::debug;
use uuid::Uuid;
pub trait ContextDbExt<T>
where
T: Serialize + for<'de> Deserialize<'de> + Debug,
{
fn from_db_record(db_context: &DbContext) -> Result<Context<T>, ContextError>;
fn to_new_db_record(&self) -> Result<NewDbContext, ContextError>;
fn to_db_record(&self, id: Uuid) -> Result<DbContext, ContextError>;
}
impl<T> ContextDbExt<T> for Context<T>
where
T: Serialize + for<'de> Deserialize<'de> + Debug,
{
fn from_db_record(db_context: &DbContext) -> Result<Context<T>, ContextError> {
debug!("Creating context from database record");
Ok(Context::from_json(db_context.value.clone())?)
}
fn to_new_db_record(&self) -> Result<NewDbContext, ContextError> {
debug!("Converting context to database record");
let json = self.to_json()?;
Ok(NewDbContext { value: json })
}
fn to_db_record(&self, id: Uuid) -> Result<DbContext, ContextError> {
debug!("Converting context to full database record");
let json = self.to_json()?;
let now = chrono::Utc::now();
Ok(DbContext {
id: UniversalUuid(id),
value: json,
created_at: UniversalTimestamp(now),
updated_at: UniversalTimestamp(now),
})
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::init_test_logging;
use chrono::{TimeZone, Utc};
fn setup_test_context() -> Context<i32> {
init_test_logging();
Context::new()
}
#[test]
fn test_context_operations() {
let mut context = setup_test_context();
assert!(context.data().is_empty());
context.insert("test", 42).unwrap();
assert_eq!(context.get("test"), Some(&42));
assert!(matches!(
context.insert("test", 43),
Err(cloacina_workflow::ContextError::KeyExists(_))
));
context.update("test", 43).unwrap();
assert_eq!(context.get("test"), Some(&43));
assert!(matches!(
context.update("nonexistent", 42),
Err(cloacina_workflow::ContextError::KeyNotFound(_))
));
}
#[test]
fn test_context_serialization() {
let mut context = setup_test_context();
context.insert("test", 42).unwrap();
let json = context.to_json().unwrap();
let deserialized = Context::<i32>::from_json(json).unwrap();
assert_eq!(deserialized.get("test"), Some(&42));
}
#[test]
fn test_context_db_conversion() {
let mut context = setup_test_context();
context.insert("test", 42).unwrap();
let json = context.to_json().unwrap();
let now = Utc::now().naive_utc();
let id = Uuid::new_v4();
let db_context = DbContext {
id: UniversalUuid(id),
value: json,
created_at: UniversalTimestamp(Utc.from_utc_datetime(&now)),
updated_at: UniversalTimestamp(Utc.from_utc_datetime(&now)),
};
let deserialized = Context::<i32>::from_db_record(&db_context).unwrap();
assert_eq!(deserialized.get("test"), Some(&42));
let new_record = context.to_new_db_record().unwrap();
assert!(!new_record.value.is_empty());
let full_record = context.to_db_record(id).unwrap();
assert_eq!(full_record.id, UniversalUuid(id));
assert!(!full_record.value.is_empty());
let roundtrip = Context::<i32>::from_db_record(&full_record).unwrap();
assert_eq!(roundtrip.get("test"), Some(&42));
}
}