surreal_sync_core/checkpoint/file.rs
1//! Checkpoint file wrapper for storage-agnostic serialization.
2
3use chrono::{DateTime, Utc};
4use serde::{Deserialize, Serialize};
5
6use crate::checkpoint::{Checkpoint, SyncPhase};
7
8/// Storage-agnostic checkpoint file wrapper.
9///
10/// This struct wraps database-specific checkpoints with metadata
11/// for storage and retrieval. The format is designed to be:
12/// - Self-describing (includes `database_type` field)
13/// - Extensible (uses JSON Value for checkpoint data)
14/// - Storage-agnostic (can be saved to local fs, remote storage, etc.)
15///
16/// # File Format
17///
18/// ```json
19/// {
20/// "database_type": "mongodb",
21/// "checkpoint": {
22/// "resume_token": [1, 2, 3],
23/// "timestamp": "2024-01-01T00:00:00Z"
24/// },
25/// "phase": "FullSyncStart",
26/// "created_at": "2024-01-01T00:00:00Z"
27/// }
28/// ```
29#[derive(Debug, Clone, Serialize, Deserialize)]
30pub struct CheckpointFile {
31 /// Database type identifier (e.g., "mongodb", "neo4j")
32 pub database_type: String,
33 /// Serialized checkpoint data as JSON Value
34 pub checkpoint: serde_json::Value,
35 /// Sync phase when this checkpoint was created
36 pub phase: SyncPhase,
37 /// Timestamp when this checkpoint file was created
38 pub created_at: DateTime<Utc>,
39}
40
41impl CheckpointFile {
42 /// Create new checkpoint file from database-specific checkpoint.
43 ///
44 /// # Arguments
45 ///
46 /// * `checkpoint` - Database-specific checkpoint implementing `Checkpoint` trait
47 /// * `phase` - Sync phase (FullSyncStart, FullSyncEnd)
48 ///
49 /// # Example
50 ///
51 /// ```rust,ignore
52 /// let checkpoint = MongoDBCheckpoint { ... };
53 /// let file = CheckpointFile::new(&checkpoint, SyncPhase::FullSyncStart)?;
54 /// ```
55 pub fn new<C: Checkpoint>(checkpoint: &C, phase: SyncPhase) -> anyhow::Result<Self> {
56 Ok(Self {
57 database_type: C::DATABASE_TYPE.to_string(),
58 checkpoint: serde_json::to_value(checkpoint)?,
59 phase,
60 created_at: Utc::now(),
61 })
62 }
63
64 /// Parse checkpoint into database-specific type.
65 ///
66 /// Validates that the stored `database_type` matches the expected type `C`.
67 ///
68 /// # Errors
69 ///
70 /// Returns an error if:
71 /// - The `database_type` doesn't match `C::DATABASE_TYPE`
72 /// - The checkpoint data can't be deserialized into type `C`
73 ///
74 /// # Example
75 ///
76 /// ```rust,ignore
77 /// let file: CheckpointFile = /* loaded from file */;
78 /// let checkpoint: MongoDBCheckpoint = file.parse()?;
79 /// ```
80 pub fn parse<C: Checkpoint>(&self) -> anyhow::Result<C> {
81 if self.database_type != C::DATABASE_TYPE {
82 anyhow::bail!(
83 "Checkpoint type mismatch: expected '{}', found '{}'",
84 C::DATABASE_TYPE,
85 self.database_type
86 );
87 }
88 Ok(serde_json::from_value(self.checkpoint.clone())?)
89 }
90
91 /// Get the database type of this checkpoint file.
92 pub fn database_type(&self) -> &str {
93 &self.database_type
94 }
95
96 /// Get the phase when this checkpoint was created.
97 pub fn phase(&self) -> &SyncPhase {
98 &self.phase
99 }
100
101 /// Get the timestamp when this checkpoint file was created.
102 pub fn created_at(&self) -> DateTime<Utc> {
103 self.created_at
104 }
105}