use anyhow::{Result, anyhow};
use std::collections::HashSet;
use std::fs;
use std::path::Path;
pub fn write_friends(path: &str, usernames: &[String]) -> Result<()> {
if let Some(parent) = Path::new(path).parent() {
if !parent.as_os_str().is_empty() {
fs::create_dir_all(parent).map_err(|e| anyhow!("friends cache mkdir: {e}"))?;
}
}
let mut body = usernames.join("\n");
if !body.is_empty() {
body.push('\n');
}
fs::write(path, body).map_err(|e| anyhow!("friends cache write {path}: {e}"))?;
Ok(())
}
pub fn read_friends(path: &str) -> Result<Vec<String>> {
let text = match fs::read_to_string(path) {
Ok(t) => t,
Err(e) if e.kind() == std::io::ErrorKind::NotFound => return Ok(Vec::new()),
Err(e) => return Err(anyhow!("friends cache read {path}: {e}")),
};
let mut seen = HashSet::new();
let mut out = Vec::new();
for line in text.lines() {
let line = line.trim();
if line.is_empty() || line.starts_with('#') {
continue;
}
let name = line.strip_prefix('@').unwrap_or(line).trim();
if name.is_empty() {
continue;
}
if seen.insert(name.to_string()) {
out.push(name.to_string());
}
}
Ok(out)
}
#[cfg(test)]
mod tests {
use super::*;
fn tmp(name: &str) -> String {
let dir = std::env::temp_dir().join("instagrab-cache-test");
fs::create_dir_all(&dir).unwrap();
dir.join(name).to_string_lossy().into_owned()
}
#[test]
fn round_trip_and_at_stripping() {
let p = tmp("friends.txt");
write_friends(&p, &["@a".to_string(), "b".to_string()]).unwrap();
assert_eq!(read_friends(&p).unwrap(), vec!["a", "b"]);
}
#[test]
fn skips_comments_blanks_and_dupes() {
let p = tmp("friends2.txt");
fs::write(&p, "# header\n\n@zuck\nzuck\n plain \n@\n").unwrap();
assert_eq!(read_friends(&p).unwrap(), vec!["zuck", "plain"]);
}
#[test]
fn missing_file_is_empty() {
let p = tmp("does-not-exist.txt");
let _ = fs::remove_file(&p);
assert_eq!(read_friends(&p).unwrap(), Vec::<String>::new());
}
}