use std::collections::HashMap;
use std::io::Write as _;
use std::path::PathBuf;
pub fn particle_of(text: &str) -> [u8; 32] {
let h = hemera::hash(text.as_bytes());
let b = h.as_bytes();
let mut out = [0u8; 32];
let n = b.len().min(32);
out[..n].copy_from_slice(&b[..n]);
out
}
pub fn com_anchor() -> [u8; 32] {
particle_of("com")
}
fn store_path() -> PathBuf {
let home = std::env::var("HOME").unwrap_or_else(|_| ".".into());
PathBuf::from(home).join("cyb").join("particles.jsonl")
}
pub fn remember(text: &str) {
static ANCHORS: std::sync::Once = std::sync::Once::new();
ANCHORS.call_once(|| {
append("com");
});
append(text);
}
fn append(text: &str) {
let path = store_path();
if let Some(dir) = path.parent() {
let _ = std::fs::create_dir_all(dir);
}
let hex: String = particle_of(text)
.iter()
.map(|b| format!("{b:02x}"))
.collect();
let escaped = text
.replace('\\', "\\\\")
.replace('"', "\\\"")
.replace('\n', "\\n");
if let Ok(mut f) = std::fs::OpenOptions::new().create(true).append(true).open(&path) {
let _ = writeln!(f, "{{\"particle\":\"{hex}\",\"text\":\"{escaped}\"}}");
}
}
pub fn load() -> HashMap<[u8; 32], String> {
let mut map = HashMap::new();
let Ok(body) = std::fs::read_to_string(store_path()) else { return map };
for line in body.lines() {
let Some(hex) = json_field(line, "particle") else { continue };
let Some(text) = json_field(line, "text") else { continue };
if hex.len() != 64 {
continue;
}
let mut hash = [0u8; 32];
let ok = (0..32).all(|i| {
u8::from_str_radix(&hex[i * 2..i * 2 + 2], 16)
.map(|b| hash[i] = b)
.is_ok()
});
if ok {
map.insert(hash, text);
}
}
map
}
pub fn json_field(line: &str, name: &str) -> Option<String> {
let key = format!("\"{name}\":\"");
let start = line.find(&key)? + key.len();
let mut out = String::new();
let mut chars = line[start..].chars();
while let Some(c) = chars.next() {
match c {
'\\' => match chars.next() {
Some('n') => out.push('\n'),
Some(other) => out.push(other),
None => return None,
},
'"' => return Some(out),
_ => out.push(c),
}
}
None
}