use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use serde_json::Value;
use uuid::Uuid;
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Signal {
pub id: Uuid,
pub name: String,
pub key: String,
pub payload: Value,
pub idempotency_id: Option<String>,
pub received_at: DateTime<Utc>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct NewSignal {
pub name: String,
pub key: String,
pub payload: Value,
pub idempotency_id: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum SignalInsert {
Created(Signal),
Duplicate(Signal),
}
impl SignalInsert {
pub fn signal(&self) -> &Signal {
match self {
SignalInsert::Created(signal) | SignalInsert::Duplicate(signal) => signal,
}
}
pub fn is_duplicate(&self) -> bool {
matches!(self, SignalInsert::Duplicate(_))
}
}
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
pub struct SignalFilter {
pub name: Option<String>,
pub key: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(tag = "outcome", rename_all = "snake_case")]
pub enum SignalStepResolution {
Resolved {
run_id: Uuid,
run_resumed: bool,
},
NotWaiting {
output: Option<Value>,
},
}
#[cfg(test)]
mod tests {
use serde_json::{from_value, json, to_value};
use super::*;
fn signal() -> Signal {
Signal {
id: Uuid::now_v7(),
name: "demo.done".to_string(),
key: "k1".to_string(),
payload: json!({"ok": true}),
idempotency_id: None,
received_at: Utc::now(),
}
}
#[test]
fn signal_insert_exposes_the_signal() {
let s = signal();
assert_eq!(SignalInsert::Created(s.clone()).signal(), &s);
assert_eq!(SignalInsert::Duplicate(s.clone()).signal(), &s);
assert!(!SignalInsert::Created(s.clone()).is_duplicate());
assert!(SignalInsert::Duplicate(s).is_duplicate());
}
#[test]
fn signal_step_resolution_serde_roundtrip() {
let resolved = SignalStepResolution::Resolved {
run_id: Uuid::now_v7(),
run_resumed: false,
};
let json = to_value(&resolved).unwrap();
assert_eq!(json["outcome"], "resolved");
assert_eq!(from_value::<SignalStepResolution>(json).unwrap(), resolved);
let not_waiting = SignalStepResolution::NotWaiting {
output: Some(json!({"timed_out": true})),
};
let json = to_value(¬_waiting).unwrap();
assert_eq!(json["outcome"], "not_waiting");
assert_eq!(
from_value::<SignalStepResolution>(json).unwrap(),
not_waiting
);
}
#[test]
fn signal_serde_roundtrip() {
let s = signal();
let back: Signal = from_value(to_value(&s).unwrap()).unwrap();
assert_eq!(back, s);
}
}