use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::path::PathBuf;
use uuid::Uuid;
use common::crypto::BLAKE3_HASH_SIZE;
use common::linked_data::{Hash, Link};
pub const CLONE_STATE_DIR: &str = ".jax";
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CloneConfig {
pub bucket_id: Uuid,
pub bucket_name: String,
pub last_synced_link: Link,
pub last_synced_height: u64,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct PathHashMap {
pub entries: HashMap<PathBuf, (Hash, [u8; BLAKE3_HASH_SIZE])>,
}
impl PathHashMap {
pub fn new() -> Self {
Self {
entries: HashMap::new(),
}
}
pub fn insert(
&mut self,
path: PathBuf,
blob_hash: Hash,
plaintext_hash: [u8; BLAKE3_HASH_SIZE],
) {
self.entries.insert(path, (blob_hash, plaintext_hash));
}
}
pub struct CloneStateManager {
root_dir: PathBuf,
}
impl CloneStateManager {
pub fn new(root_dir: PathBuf) -> Self {
Self { root_dir }
}
pub fn state_dir(&self) -> PathBuf {
self.root_dir.join(CLONE_STATE_DIR)
}
fn config_path(&self) -> PathBuf {
self.state_dir().join("config.json")
}
fn hash_map_path(&self) -> PathBuf {
self.state_dir().join("hashes.json")
}
pub fn init(&self, config: CloneConfig) -> Result<(), CloneStateError> {
let state_dir = self.state_dir();
std::fs::create_dir_all(&state_dir)?;
self.write_config(&config)?;
self.write_hash_map(&PathHashMap::new())?;
Ok(())
}
pub fn is_initialized(&self) -> bool {
self.config_path().exists()
}
pub fn read_config(&self) -> Result<CloneConfig, CloneStateError> {
let config_data = std::fs::read_to_string(self.config_path())?;
let config: CloneConfig = serde_json::from_str(&config_data)?;
Ok(config)
}
pub fn write_config(&self, config: &CloneConfig) -> Result<(), CloneStateError> {
let config_json = serde_json::to_string_pretty(config)?;
std::fs::write(self.config_path(), config_json)?;
Ok(())
}
pub fn write_hash_map(&self, hash_map: &PathHashMap) -> Result<(), CloneStateError> {
let hash_map_json = serde_json::to_string_pretty(hash_map)?;
std::fs::write(self.hash_map_path(), hash_map_json)?;
Ok(())
}
pub fn update_sync_state(&self, link: Link, height: u64) -> Result<(), CloneStateError> {
let mut config = self.read_config()?;
config.last_synced_link = link;
config.last_synced_height = height;
self.write_config(&config)?;
Ok(())
}
}
#[derive(Debug, thiserror::Error)]
pub enum CloneStateError {
#[error("I/O error: {0}")]
Io(#[from] std::io::Error),
#[error("JSON error: {0}")]
Json(#[from] serde_json::Error),
#[error("Clone directory not initialized (no .jax directory found)")]
NotInitialized,
}