use super::KindleDb;
use crate::{KeyError, Result};
pub fn parse_k4i(bytes: &[u8]) -> Result<KindleDb> {
let value: serde_json::Value = serde_json::from_slice(bytes)
.map_err(|e| KeyError::Invalid(format!("invalid .k4i JSON: {e}")))?;
let obj = value
.as_object()
.ok_or_else(|| KeyError::Invalid("`.k4i` root is not a JSON object".into()))?;
let mut db = KindleDb::with_capacity(obj.len());
for (name, val) in obj {
let hex = val.as_str().ok_or_else(|| {
KeyError::Invalid(format!("`.k4i` value for `{name}` is not a string"))
})?;
db.insert(name.clone(), hex.to_string());
}
Ok(db)
}
pub fn load_k4i(path: &std::path::Path) -> Result<KindleDb> {
let bytes = std::fs::read(path)
.map_err(|e| KeyError::NotFound(format!("cannot read {}: {e}", path.display())))?;
parse_k4i(&bytes)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn parses_flat_hex_map() {
let json = br#"{"DSN":"aabbccdd","kindle.account.tokens":"0011"}"#;
let db = parse_k4i(json).unwrap();
assert_eq!(db.get("DSN"), Some(&"aabbccdd".to_string()));
assert_eq!(db.get("kindle.account.tokens"), Some(&"0011".to_string()));
assert_eq!(db.len(), 2);
}
#[test]
fn rejects_non_object_root() {
assert!(parse_k4i(b"[1,2,3]").is_err());
assert!(parse_k4i(b"not json").is_err());
}
#[test]
fn rejects_non_string_value() {
assert!(parse_k4i(br#"{"DSN":123}"#).is_err());
}
}