use std::collections::HashMap;
use async_trait::async_trait;
use serde::{Deserialize, Serialize};
use synaptic_core::SynapticError;
#[derive(Debug, Clone, Hash, Eq, PartialEq, Serialize, Deserialize)]
pub struct CheckpointConfig {
pub thread_id: String,
pub checkpoint_id: Option<String>,
}
impl CheckpointConfig {
pub fn new(thread_id: impl Into<String>) -> Self {
Self {
thread_id: thread_id.into(),
checkpoint_id: None,
}
}
pub fn with_checkpoint_id(
thread_id: impl Into<String>,
checkpoint_id: impl Into<String>,
) -> Self {
Self {
thread_id: thread_id.into(),
checkpoint_id: Some(checkpoint_id.into()),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Checkpoint {
pub id: String,
pub state: serde_json::Value,
pub next_node: Option<String>,
pub parent_id: Option<String>,
pub metadata: HashMap<String, serde_json::Value>,
}
impl Checkpoint {
pub fn new(state: serde_json::Value, next_node: Option<String>) -> Self {
Self {
id: generate_checkpoint_id(),
state,
next_node,
parent_id: None,
metadata: HashMap::new(),
}
}
pub fn with_parent(mut self, parent_id: impl Into<String>) -> Self {
self.parent_id = Some(parent_id.into());
self
}
pub fn with_metadata(mut self, key: impl Into<String>, value: serde_json::Value) -> Self {
self.metadata.insert(key.into(), value);
self
}
}
fn generate_checkpoint_id() -> String {
use std::sync::atomic::{AtomicU64, Ordering};
use std::time::{SystemTime, UNIX_EPOCH};
static COUNTER: AtomicU64 = AtomicU64::new(0);
let ts = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap_or_default()
.as_nanos();
let seq = COUNTER.fetch_add(1, Ordering::Relaxed);
format!("{ts:x}-{seq:04x}")
}
#[async_trait]
pub trait Checkpointer: Send + Sync {
async fn put(
&self,
config: &CheckpointConfig,
checkpoint: &Checkpoint,
) -> Result<(), SynapticError>;
async fn get(&self, config: &CheckpointConfig) -> Result<Option<Checkpoint>, SynapticError>;
async fn list(&self, config: &CheckpointConfig) -> Result<Vec<Checkpoint>, SynapticError>;
}