use crate::checkpoint::{
store::CheckpointStore, Checkpoint, CheckpointID, StoredCheckpoint, SyncPhase,
};
pub struct SyncManager<S: CheckpointStore> {
store: S,
emit_checkpoints: bool,
}
impl<S: CheckpointStore> SyncManager<S> {
pub fn new(store: S) -> Self {
Self {
store,
emit_checkpoints: true,
}
}
pub fn new_without_emit(store: S) -> Self {
Self {
store,
emit_checkpoints: false,
}
}
pub fn with_emit_checkpoints(mut self, emit: bool) -> Self {
self.emit_checkpoints = emit;
self
}
pub fn emit_checkpoints(&self) -> bool {
self.emit_checkpoints
}
pub fn store(&self) -> &S {
&self.store
}
pub async fn emit_checkpoint<C: Checkpoint>(
&self,
checkpoint: &C,
phase: SyncPhase,
) -> anyhow::Result<()> {
if !self.emit_checkpoints {
return Ok(());
}
let id = CheckpointID {
database_type: C::DATABASE_TYPE.to_string(),
phase: phase.as_str().to_string(),
};
let checkpoint_data = serde_json::to_string(checkpoint)?;
self.store.store_checkpoint(&id, checkpoint_data).await?;
tracing::info!(
"Emitted {} checkpoint: {}",
phase,
checkpoint.to_cli_string()
);
Ok(())
}
pub async fn read_checkpoint<C: Checkpoint>(&self, phase: SyncPhase) -> anyhow::Result<C> {
let id = CheckpointID {
database_type: C::DATABASE_TYPE.to_string(),
phase: phase.as_str().to_string(),
};
let stored = self
.store
.read_checkpoint(&id)
.await?
.ok_or_else(|| anyhow::anyhow!("No checkpoint found for phase: {phase}"))?;
if stored.database_type != C::DATABASE_TYPE {
return Err(anyhow::anyhow!(
"Checkpoint database type mismatch: expected '{}', found '{}'",
C::DATABASE_TYPE,
stored.database_type
));
}
Ok(serde_json::from_str(&stored.checkpoint_data)?)
}
}
pub struct NullStore;
#[async_trait::async_trait]
impl CheckpointStore for NullStore {
async fn store_checkpoint(
&self,
_id: &CheckpointID,
_checkpoint_data: String,
) -> anyhow::Result<()> {
Ok(())
}
async fn read_checkpoint(
&self,
_id: &CheckpointID,
) -> anyhow::Result<Option<StoredCheckpoint>> {
Ok(None)
}
}
pub type NullSyncManager = SyncManager<NullStore>;
impl NullSyncManager {
pub fn disabled() -> Self {
SyncManager::new_without_emit(NullStore)
}
}