jax_daemon/
clone_state.rs1use serde::{Deserialize, Serialize};
7use std::collections::HashMap;
8use std::path::PathBuf;
9use uuid::Uuid;
10
11use common::crypto::BLAKE3_HASH_SIZE;
12use common::linked_data::{Hash, Link};
13
14pub const CLONE_STATE_DIR: &str = ".jax";
16
17#[derive(Debug, Clone, Serialize, Deserialize)]
19pub struct CloneConfig {
20 pub bucket_id: Uuid,
22 pub bucket_name: String,
24 pub last_synced_link: Link,
26 pub last_synced_height: u64,
28}
29
30#[derive(Debug, Clone, Serialize, Deserialize, Default)]
33pub struct PathHashMap {
34 pub entries: HashMap<PathBuf, (Hash, [u8; BLAKE3_HASH_SIZE])>,
36}
37
38impl PathHashMap {
39 pub fn new() -> Self {
40 Self {
41 entries: HashMap::new(),
42 }
43 }
44
45 pub fn insert(
46 &mut self,
47 path: PathBuf,
48 blob_hash: Hash,
49 plaintext_hash: [u8; BLAKE3_HASH_SIZE],
50 ) {
51 self.entries.insert(path, (blob_hash, plaintext_hash));
52 }
53}
54
55pub struct CloneStateManager {
57 root_dir: PathBuf,
59}
60
61impl CloneStateManager {
62 pub fn new(root_dir: PathBuf) -> Self {
64 Self { root_dir }
65 }
66
67 pub fn state_dir(&self) -> PathBuf {
69 self.root_dir.join(CLONE_STATE_DIR)
70 }
71
72 fn config_path(&self) -> PathBuf {
74 self.state_dir().join("config.json")
75 }
76
77 fn hash_map_path(&self) -> PathBuf {
79 self.state_dir().join("hashes.json")
80 }
81
82 pub fn init(&self, config: CloneConfig) -> Result<(), CloneStateError> {
84 let state_dir = self.state_dir();
85
86 std::fs::create_dir_all(&state_dir)?;
88
89 self.write_config(&config)?;
91
92 self.write_hash_map(&PathHashMap::new())?;
94
95 Ok(())
96 }
97
98 pub fn is_initialized(&self) -> bool {
100 self.config_path().exists()
101 }
102
103 pub fn read_config(&self) -> Result<CloneConfig, CloneStateError> {
105 let config_data = std::fs::read_to_string(self.config_path())?;
106 let config: CloneConfig = serde_json::from_str(&config_data)?;
107 Ok(config)
108 }
109
110 pub fn write_config(&self, config: &CloneConfig) -> Result<(), CloneStateError> {
112 let config_json = serde_json::to_string_pretty(config)?;
113 std::fs::write(self.config_path(), config_json)?;
114 Ok(())
115 }
116
117 pub fn write_hash_map(&self, hash_map: &PathHashMap) -> Result<(), CloneStateError> {
119 let hash_map_json = serde_json::to_string_pretty(hash_map)?;
120 std::fs::write(self.hash_map_path(), hash_map_json)?;
121 Ok(())
122 }
123
124 pub fn update_sync_state(&self, link: Link, height: u64) -> Result<(), CloneStateError> {
126 let mut config = self.read_config()?;
127 config.last_synced_link = link;
128 config.last_synced_height = height;
129 self.write_config(&config)?;
130 Ok(())
131 }
132}
133
134#[derive(Debug, thiserror::Error)]
135pub enum CloneStateError {
136 #[error("I/O error: {0}")]
137 Io(#[from] std::io::Error),
138 #[error("JSON error: {0}")]
139 Json(#[from] serde_json::Error),
140 #[error("Clone directory not initialized (no .jax directory found)")]
141 NotInitialized,
142}