Skip to main content

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}