use std::fmt::Debug;
use serde::{Deserialize, Serialize};
use crate::state::State;
use crate::workflow_state::WorkflowState;
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct CheckpointId(pub uuid::Uuid);
impl std::fmt::Display for CheckpointId {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.0)
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct NodeId(pub String);
impl std::fmt::Display for NodeId {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.0)
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Checkpoint<S: WorkflowState = State> {
pub checkpoint_id: CheckpointId,
pub current_node: NodeId,
pub state: S::Checkpoint,
pub graph_hash: u64,
pub created_at: std::time::SystemTime,
}
impl<S: WorkflowState> Checkpoint<S> {
pub fn new(current_node: impl Into<String>, state: &S, graph_hash: u64) -> Self {
Self {
checkpoint_id: CheckpointId(uuid::Uuid::new_v4()),
current_node: NodeId(current_node.into()),
state: state.snapshot(),
graph_hash,
created_at: std::time::SystemTime::now(),
}
}
pub fn restore_state(self) -> S {
S::restore(self.state)
}
}
#[derive(Debug, Clone)]
pub struct CheckpointBlob {
pub id: CheckpointId,
pub data: Vec<u8>,
pub graph_hash: u64,
pub created_at: std::time::SystemTime,
}
impl CheckpointBlob {
pub fn new(
id: CheckpointId,
data: Vec<u8>,
graph_hash: u64,
created_at: std::time::SystemTime,
) -> Self {
Self {
id,
data,
graph_hash,
created_at,
}
}
}
#[derive(Debug, thiserror::Error)]
pub enum CheckpointStoreError {
#[error("storage error: {0}")]
Storage(String),
#[error("checkpoint not found: {0}")]
NotFound(CheckpointId),
#[error("corrupted checkpoint: {0}")]
Corrupted(String),
#[error("serialization error: {0}")]
Serialization(String),
#[error("graph mismatch: expected hash {expected:#018x}, got {actual:#018x}")]
GraphMismatch { expected: u64, actual: u64 },
}
pub use crate::ids::TraceId;
#[allow(deprecated)]
#[doc(inline)]
pub use crate::checkpoint_policy::CheckpointPolicy;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Frame<S: WorkflowState = State> {
pub graph_id: String,
pub node_id: String,
pub state: S::Checkpoint,
pub cursor: usize,
}
impl<S: WorkflowState> Frame<S> {
pub fn new(graph_id: String, node_id: String, state: &S, cursor: usize) -> Self {
Self {
graph_id,
node_id,
state: state.snapshot(),
cursor,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FrameStack<S: WorkflowState = State>
where
S::Checkpoint: Debug,
{
frames: Vec<Frame<S>>,
}
impl<S: WorkflowState> FrameStack<S>
where
S::Checkpoint: Debug,
{
pub fn new() -> Self {
Self { frames: Vec::new() }
}
pub fn push(&mut self, frame: Frame<S>) {
self.frames.push(frame);
}
pub fn pop(&mut self) -> Option<Frame<S>> {
self.frames.pop()
}
pub fn current(&self) -> Option<&Frame<S>> {
self.frames.last()
}
pub fn depth(&self) -> usize {
self.frames.len()
}
pub fn is_empty(&self) -> bool {
self.frames.is_empty()
}
pub fn frames(&self) -> &[Frame<S>] {
&self.frames
}
}
impl<S: WorkflowState> Default for FrameStack<S>
where
S::Checkpoint: Debug,
{
fn default() -> Self {
Self::new()
}
}