86 lines
2.6 KiB
Rust
86 lines
2.6 KiB
Rust
use std::fs;
|
||
use std::io;
|
||
use std::path::{Path, PathBuf};
|
||
|
||
use crate::identity::NodeError;
|
||
|
||
pub const CURRENT_WINDOW_FILE: &str = "current_window.bin";
|
||
|
||
// Layout v1 (16 байт):
|
||
// [0..4] magic = b"mtcw"
|
||
// [4] version = 1
|
||
// [5..8] reserved = 0 (выравнивание + future flags)
|
||
// [8..16] window u64 LE
|
||
// Legacy v0 (8 байт): только window u64 LE без magic. На load auto-upgrade:
|
||
// распарсить как v0, следующий save запишет уже в v1.
|
||
const MAGIC: &[u8; 4] = b"mtcw";
|
||
const VERSION: u8 = 1;
|
||
const FILE_SIZE_V1: usize = 16;
|
||
const FILE_SIZE_V0: usize = 8;
|
||
|
||
pub fn meta_dir(data_dir: &Path) -> PathBuf {
|
||
data_dir.join("meta")
|
||
}
|
||
|
||
pub fn current_window_path(data_dir: &Path) -> PathBuf {
|
||
meta_dir(data_dir).join(CURRENT_WINDOW_FILE)
|
||
}
|
||
|
||
pub fn load_current_window(data_dir: &Path) -> Result<u64, NodeError> {
|
||
let path = current_window_path(data_dir);
|
||
if !path.exists() {
|
||
return Ok(0);
|
||
}
|
||
let bytes = fs::read(&path)?;
|
||
match bytes.len() {
|
||
FILE_SIZE_V1 => {
|
||
if &bytes[0..4] != MAGIC.as_slice() {
|
||
return Err(NodeError::InvalidMagic);
|
||
}
|
||
if bytes[4] != VERSION {
|
||
return Err(NodeError::UnsupportedVersion(bytes[4]));
|
||
}
|
||
let mut buf = [0u8; 8];
|
||
buf.copy_from_slice(&bytes[8..16]);
|
||
Ok(u64::from_le_bytes(buf))
|
||
},
|
||
FILE_SIZE_V0 => {
|
||
// Legacy без magic. Парсим как u64 LE; следующий save запишет v1.
|
||
let mut buf = [0u8; 8];
|
||
buf.copy_from_slice(&bytes);
|
||
Ok(u64::from_le_bytes(buf))
|
||
},
|
||
actual => Err(NodeError::CorruptedSize {
|
||
expected: FILE_SIZE_V1,
|
||
actual,
|
||
}),
|
||
}
|
||
}
|
||
|
||
pub fn save_current_window(data_dir: &Path, window: u64) -> Result<(), NodeError> {
|
||
fs::create_dir_all(meta_dir(data_dir))?;
|
||
let path = current_window_path(data_dir);
|
||
let tmp = path.with_extension("bin.tmp");
|
||
let mut bytes = [0u8; FILE_SIZE_V1];
|
||
bytes[0..4].copy_from_slice(MAGIC);
|
||
bytes[4] = VERSION;
|
||
bytes[8..16].copy_from_slice(&window.to_le_bytes());
|
||
fs::write(&tmp, bytes)?;
|
||
fs::rename(&tmp, &path)?;
|
||
Ok(())
|
||
}
|
||
|
||
pub fn ensure_current_window_initialized(data_dir: &Path) -> Result<u64, NodeError> {
|
||
let path = current_window_path(data_dir);
|
||
if path.exists() {
|
||
return load_current_window(data_dir);
|
||
}
|
||
save_current_window(data_dir, 0)?;
|
||
Ok(0)
|
||
}
|
||
|
||
#[allow(dead_code)]
|
||
fn _io_to_local(e: io::Error) -> NodeError {
|
||
NodeError::Io(e)
|
||
}
|