Skip to main content

osmic_repl/
state.rs

1use std::path::Path;
2
3use osmic_core::error::{OsmicError, OsmicResult};
4use serde::{Deserialize, Serialize};
5
6/// Tracks replication state: current sequence number and base URL.
7#[derive(Debug, Clone, Serialize, Deserialize)]
8pub struct ReplicationState {
9    pub sequence_number: u64,
10    pub timestamp: String,
11    pub base_url: String,
12}
13
14impl ReplicationState {
15    /// Load replication state from a state directory.
16    pub fn load(state_dir: &Path) -> OsmicResult<Self> {
17        let path = state_dir.join("state.json");
18        let data = std::fs::read_to_string(&path)
19            .map_err(|e| OsmicError::Other(format!("Failed to read state: {e}")))?;
20        serde_json::from_str(&data)
21            .map_err(|e| OsmicError::Other(format!("Failed to parse state: {e}")))
22    }
23
24    /// Save replication state to a state directory.
25    pub fn save(&self, state_dir: &Path) -> OsmicResult<()> {
26        std::fs::create_dir_all(state_dir)
27            .map_err(|e| OsmicError::Other(format!("Failed to create state dir: {e}")))?;
28        let path = state_dir.join("state.json");
29        let data = serde_json::to_string_pretty(self)
30            .map_err(|e| OsmicError::Other(format!("Failed to serialize state: {e}")))?;
31        std::fs::write(&path, data)
32            .map_err(|e| OsmicError::Other(format!("Failed to write state: {e}")))
33    }
34
35    /// Compute the URL for the next .osc.gz replication file.
36    ///
37    /// OSM replication files use a 9-digit sequence number split into
38    /// 3-digit directory segments: 000/001/234.osc.gz
39    pub fn next_osc_url(&self) -> String {
40        let seq = self.sequence_number + 1;
41        let a = seq / 1_000_000;
42        let b = (seq / 1_000) % 1_000;
43        let c = seq % 1_000;
44        format!(
45            "{}/{:03}/{:03}/{:03}.osc.gz",
46            self.base_url.trim_end_matches('/'),
47            a,
48            b,
49            c
50        )
51    }
52
53    /// Create initial state for a given base URL and sequence number.
54    pub fn init(base_url: &str, sequence_number: u64) -> Self {
55        Self {
56            sequence_number,
57            timestamp: String::new(),
58            base_url: base_url.to_string(),
59        }
60    }
61}