use anyhow::Result;
use colored::Colorize;
use crate::cli::BrowserProfileAction;
use tsafe_core::profile;
fn browser_profiles_path() -> std::path::PathBuf {
profile::vault_dir().join("browser-profiles.json")
}
fn load_browser_profiles() -> Result<serde_json::Map<String, serde_json::Value>> {
let path = browser_profiles_path();
if !path.exists() {
return Ok(serde_json::Map::new());
}
let data = std::fs::read_to_string(&path)?;
Ok(serde_json::from_str(&data)?)
}
fn save_browser_profiles(map: &serde_json::Map<String, serde_json::Value>) -> Result<()> {
let path = browser_profiles_path();
if let Some(parent) = path.parent() {
std::fs::create_dir_all(parent)?;
}
let tmp = path.with_extension("tmp");
std::fs::write(&tmp, serde_json::to_string_pretty(map)?)?;
std::fs::rename(&tmp, &path)?;
Ok(())
}
fn empty_browser_profile_list_message() -> String {
format!(
"{} No mappings. Add one: tsafe browser-profile add <domain>",
"i".blue()
)
}
pub(crate) fn cmd_browser_profile(
active_profile: &str,
action: BrowserProfileAction,
) -> Result<()> {
match action {
BrowserProfileAction::Add {
domain,
profile: vault_profile,
} => {
let target = vault_profile.as_deref().unwrap_or(active_profile);
let mut map = load_browser_profiles()?;
map.insert(
domain.clone(),
serde_json::Value::String(target.to_string()),
);
save_browser_profiles(&map)?;
println!("{} {} → profile '{target}'", "✓".green(), domain);
}
BrowserProfileAction::List => {
let map = load_browser_profiles()?;
if map.is_empty() {
println!("{}", empty_browser_profile_list_message());
} else {
println!("{:<45} Profile", "Domain");
println!("{}", "-".repeat(60));
let mut entries: Vec<_> = map.iter().collect();
entries.sort_by_key(|(d, _)| d.as_str());
for (domain, prof) in entries {
println!("{:<45} {}", domain, prof.as_str().unwrap_or("?"));
}
}
}
BrowserProfileAction::Remove { domain } => {
let mut map = load_browser_profiles()?;
if map.remove(&domain).is_some() {
save_browser_profiles(&map)?;
println!("{} Removed mapping for '{domain}'", "✓".green());
} else {
anyhow::bail!("no mapping found for '{domain}'");
}
}
}
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn empty_list_message_stays_command_local() {
let message = empty_browser_profile_list_message();
assert!(message.contains("No mappings."));
assert!(message.contains("tsafe browser-profile add <domain>"));
assert!(!message.contains("extension"));
}
}