use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use uuid::Uuid;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum SuspendReason {
Sleep { wake_at: DateTime<Utc> },
WaitingEvent {
event_name: String,
timeout: Option<DateTime<Utc>>,
},
}
impl SuspendReason {
pub fn wake_at(&self) -> Option<DateTime<Utc>> {
match self {
Self::Sleep { wake_at } => Some(*wake_at),
Self::WaitingEvent { timeout, .. } => *timeout,
}
}
pub fn is_sleep(&self) -> bool {
matches!(self, Self::Sleep { .. })
}
pub fn is_event_wait(&self) -> bool {
matches!(self, Self::WaitingEvent { .. })
}
pub fn event_name(&self) -> Option<&str> {
match self {
Self::WaitingEvent { event_name, .. } => Some(event_name),
_ => None,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct WorkflowEvent {
pub id: Uuid,
pub event_name: String,
pub correlation_id: String,
pub payload: Option<serde_json::Value>,
pub created_at: DateTime<Utc>,
}
impl WorkflowEvent {
pub fn new(
event_name: impl Into<String>,
correlation_id: impl Into<String>,
payload: Option<serde_json::Value>,
) -> Self {
Self {
id: Uuid::new_v4(),
event_name: event_name.into(),
correlation_id: correlation_id.into(),
payload,
created_at: Utc::now(),
}
}
pub fn payload_as<T: serde::de::DeserializeOwned>(&self) -> Option<T> {
self.payload
.as_ref()
.and_then(|p| serde_json::from_value(p.clone()).ok())
}
}
#[cfg(test)]
#[allow(clippy::unwrap_used, clippy::indexing_slicing)]
mod tests {
use super::*;
#[test]
fn test_suspend_reason_sleep() {
let wake_at = Utc::now() + chrono::Duration::hours(1);
let reason = SuspendReason::Sleep { wake_at };
assert!(reason.is_sleep());
assert!(!reason.is_event_wait());
assert_eq!(reason.wake_at(), Some(wake_at));
assert!(reason.event_name().is_none());
}
#[test]
fn test_suspend_reason_event() {
let timeout = Utc::now() + chrono::Duration::days(7);
let reason = SuspendReason::WaitingEvent {
event_name: "payment_confirmed".to_string(),
timeout: Some(timeout),
};
assert!(!reason.is_sleep());
assert!(reason.is_event_wait());
assert_eq!(reason.wake_at(), Some(timeout));
assert_eq!(reason.event_name(), Some("payment_confirmed"));
}
#[test]
fn test_workflow_event_creation() {
let event = WorkflowEvent::new(
"order_completed",
"workflow-123",
Some(serde_json::json!({"order_id": "ABC123"})),
);
assert_eq!(event.event_name, "order_completed");
assert_eq!(event.correlation_id, "workflow-123");
assert!(event.payload.is_some());
}
#[test]
fn test_workflow_event_payload_typed() {
#[derive(Debug, Deserialize, PartialEq)]
struct OrderData {
order_id: String,
}
let event = WorkflowEvent::new(
"order_completed",
"workflow-123",
Some(serde_json::json!({"order_id": "ABC123"})),
);
let data: Option<OrderData> = event.payload_as();
assert!(data.is_some());
assert_eq!(
data.unwrap(),
OrderData {
order_id: "ABC123".to_string()
}
);
}
}