// Shared utilities use crate::store; use serde::de::DeserializeOwned; use serde::Serialize; use std::fs; use std::io::Write; use std::path::{Path, PathBuf}; /// Ensure a subdirectory of the memory dir exists and return its path. pub fn memory_subdir(name: &str) -> Result { let dir = store::memory_dir().join(name); fs::create_dir_all(&dir) .map_err(|e| format!("create {}: {}", dir.display(), e))?; Ok(dir) } /// Truncate text to `max_len` bytes at a char boundary, appending `suffix`. /// Returns the original string if it's already short enough. pub fn truncate(text: &str, max_len: usize, suffix: &str) -> String { if text.len() <= max_len { text.to_string() } else { let end = text.floor_char_boundary(max_len); format!("{}{}", &text[..end], suffix) } } /// Take the first `n` chars from a string. pub fn first_n_chars(s: &str, n: usize) -> String { s.chars().take(n).collect() } // ── JSONL helpers ─────────────────────────────────────────────────── /// Read a JSONL file, deserializing each line. Silently skips bad lines. pub fn jsonl_load(path: &Path) -> Vec { let content = match fs::read_to_string(path) { Ok(c) => c, Err(_) => return Vec::new(), }; content.lines() .filter_map(|line| serde_json::from_str(line).ok()) .collect() } /// Append one record as a JSON line to a file (create if missing). pub fn jsonl_append(path: &Path, item: &T) -> Result<(), String> { let json = serde_json::to_string(item) .map_err(|e| format!("serialize: {}", e))?; let mut f = fs::OpenOptions::new() .create(true).append(true).open(path) .map_err(|e| format!("open {}: {}", path.display(), e))?; writeln!(f, "{}", json) .map_err(|e| format!("write {}: {}", path.display(), e)) }