use std::path::{Path, PathBuf}; use color_eyre::Result; /// ~/.bore directory. pub fn bore_dir() -> Result { let home = dirs::home_dir() .ok_or_else(|| color_eyre::eyre::eyre!("could not determine home directory"))?; Ok(home.join(".bore")) } fn secrets_path() -> Result { Ok(bore_dir()?.join("secrets")) } pub fn load_secret(server_key: &str) -> Result> { let path = secrets_path()?; load_entry(&path, server_key) } pub fn save_secret(server_key: &str, secret: &str) -> Result<()> { let path = secrets_path()?; save_entry(&path, server_key, secret) } pub fn load_entry(path: &Path, key: &str) -> Result> { let contents = match std::fs::read_to_string(path) { Ok(c) => c, Err(e) if e.kind() == std::io::ErrorKind::NotFound => return Ok(None), Err(e) => return Err(e.into()), }; for line in contents.lines() { let line = line.trim(); if line.is_empty() || line.starts_with('#') { continue; } if let Some((k, v)) = line.split_once(' ') && k == key { return Ok(Some(v.to_string())); } } Ok(None) } pub fn save_entry(path: &Path, key: &str, value: &str) -> Result<()> { if let Some(parent) = path.parent() { std::fs::create_dir_all(parent)?; } let mut lines: Vec = match std::fs::read_to_string(path) { Ok(c) => c .lines() .filter(|l| l.split_once(' ').is_none_or(|(k, _)| k != key)) .map(String::from) .collect(), Err(e) if e.kind() == std::io::ErrorKind::NotFound => Vec::new(), Err(e) => return Err(e.into()), }; lines.push(format!("{key} {value}")); std::fs::write(path, lines.join("\n") + "\n")?; Ok(()) }