use super::super::error::TunerError;
use super::super::helpers::crc32_update;
use super::BrickTuner;
impl BrickTuner {
const APR_MAGIC: [u8; 4] = [b'A', b'P', b'R', b'1'];
pub fn to_json(&self) -> Result<String, TunerError> {
serde_json::to_string_pretty(self).map_err(|e| TunerError::Serialization(e.to_string()))
}
pub fn from_json(json: &str) -> Result<Self, TunerError> {
serde_json::from_str(json).map_err(|e| TunerError::Serialization(e.to_string()))
}
#[cfg(feature = "hardware-detect")]
pub fn cache_path() -> std::path::PathBuf {
let cache_dir =
dirs::cache_dir().unwrap_or_else(|| std::path::PathBuf::from(".")).join("trueno");
let _ = std::fs::create_dir_all(&cache_dir);
cache_dir.join(format!("tuner_model_v{}.apr", Self::VERSION))
}
#[cfg(feature = "hardware-detect")]
pub fn load_or_default() -> Self {
let path = Self::cache_path();
if path.exists() {
match Self::load_apr(&path) {
Ok(tuner) => {
if tuner.version == Self::VERSION {
return tuner;
}
}
Err(_) => {
}
}
}
Self::new()
}
pub fn save_apr<P: AsRef<std::path::Path>>(&self, path: P) -> Result<(), TunerError> {
use std::io::Write;
let json = self.to_json()?;
let json_bytes = json.as_bytes();
let mut file = std::fs::File::create(path).map_err(|e| TunerError::Io(e.to_string()))?;
file.write_all(&Self::APR_MAGIC).map_err(|e| TunerError::Io(e.to_string()))?;
let len = json_bytes.len() as u32;
file.write_all(&len.to_le_bytes()).map_err(|e| TunerError::Io(e.to_string()))?;
file.write_all(json_bytes).map_err(|e| TunerError::Io(e.to_string()))?;
let mut crc = 0u32;
crc = crc32_update(crc, &Self::APR_MAGIC);
crc = crc32_update(crc, &len.to_le_bytes());
crc = crc32_update(crc, json_bytes);
file.write_all(&crc.to_le_bytes()).map_err(|e| TunerError::Io(e.to_string()))?;
Ok(())
}
pub fn load_apr<P: AsRef<std::path::Path>>(path: P) -> Result<Self, TunerError> {
use std::io::Read;
let mut file = std::fs::File::open(path).map_err(|e| TunerError::Io(e.to_string()))?;
let mut magic = [0u8; 4];
file.read_exact(&mut magic).map_err(|e| TunerError::Io(e.to_string()))?;
if magic != Self::APR_MAGIC {
return Err(TunerError::InvalidFormat("Invalid APR magic bytes".to_string()));
}
let mut len_bytes = [0u8; 4];
file.read_exact(&mut len_bytes).map_err(|e| TunerError::Io(e.to_string()))?;
let len = u32::from_le_bytes(len_bytes) as usize;
let mut json_bytes = vec![0u8; len];
file.read_exact(&mut json_bytes).map_err(|e| TunerError::Io(e.to_string()))?;
let mut crc_bytes = [0u8; 4];
file.read_exact(&mut crc_bytes).map_err(|e| TunerError::Io(e.to_string()))?;
let stored_crc = u32::from_le_bytes(crc_bytes);
let mut computed_crc = 0u32;
computed_crc = crc32_update(computed_crc, &Self::APR_MAGIC);
computed_crc = crc32_update(computed_crc, &len_bytes);
computed_crc = crc32_update(computed_crc, &json_bytes);
if stored_crc != computed_crc {
return Err(TunerError::InvalidFormat("CRC32 checksum mismatch".to_string()));
}
let json =
String::from_utf8(json_bytes).map_err(|e| TunerError::Serialization(e.to_string()))?;
Self::from_json(&json)
}
#[cfg(feature = "hardware-detect")]
pub fn save_to_cache(&self) -> Result<(), TunerError> {
self.save_apr(Self::cache_path())
}
}