use chamber_vault::{VaultCategory, VaultManager};
use clap::Subcommand;
use color_eyre::eyre::eyre;
use std::path::PathBuf;
#[derive(Subcommand, Debug)]
pub enum VaultCommand {
List,
Create {
name: String,
#[arg(long)]
path: Option<PathBuf>,
#[arg(long, default_value = "personal")]
category: String,
#[arg(long)]
description: Option<String>,
},
Switch {
vault: String,
},
Active,
Delete {
vault: String,
#[arg(long)]
delete_file: bool,
},
Import {
file: PathBuf,
name: String,
#[arg(long, default_value = "personal")]
category: String,
#[arg(long)]
copy: bool,
},
Info { vault: Option<String> },
Update {
vault: String,
#[arg(long)]
name: Option<String>,
#[arg(long)]
description: Option<String>,
#[arg(long)]
category: Option<String>,
#[arg(long)]
favorite: Option<bool>,
},
}
#[allow(clippy::too_many_lines)]
pub fn handle_vault_command(manager: &mut VaultManager, cmd: crate::VaultCommand) -> color_eyre::Result<()> {
match cmd {
crate::VaultCommand::List => {
let manager = VaultManager::new()?;
let vaults = manager.list_vaults();
if vaults.is_empty() {
println!("No vaults found. Create your first vault with:");
println!(" chamber registry create <name>");
return Ok(());
}
println!("📁 Available Vaults:");
println!("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
for vault in vaults {
let status = if manager.is_vault_open(&vault.id) {
"Open"
} else {
"Closed"
};
let active_indicator = if Some(&vault.id) == manager.registry.active_vault_id.as_ref() {
" ← Active"
} else {
""
};
println!("ID: {} Name: {}{}", vault.id, vault.name, active_indicator);
println!("Category: {} Status: {}", vault.category, status);
if let Some(desc) = &vault.description {
println!("Description: {desc}");
}
println!("Path: {}", vault.path.display());
println!("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
}
}
crate::VaultCommand::Create {
name,
path,
category,
description,
} => {
let category = parse_category(&category);
let password = rpassword::prompt_password("Enter master password for new vault: ")?;
let vault_id = manager.create_vault(name.clone(), path, category, description, &password)?;
println!("Created vault '{name}' with ID: {vault_id}");
}
crate::VaultCommand::Switch { vault } => {
let vault_id = find_vault_id(manager, &vault)?;
manager.switch_active_vault(&vault_id)?;
println!("Switched to vault: {vault_id}");
}
crate::VaultCommand::Active => {
if let Some(active) = manager.registry.get_active_vault() {
println!("Active vault: {} ({})", active.name, active.id);
println!(" Path: {}", active.path.display());
println!(" Category: {}", active.category);
if let Some(desc) = &active.description {
println!(" Description: {desc}");
}
} else {
println!("No active vault");
}
}
crate::VaultCommand::Delete { vault, delete_file } => {
let vault_id = find_vault_id(manager, &vault)?;
let is_active_vault = manager.registry.active_vault_id.as_ref() == Some(&vault_id);
let vault_count = manager.registry.vaults.len();
if is_active_vault && vault_count > 1 {
println!("❌ Cannot delete the active vault while other vaults exist.");
println!("💡 Switch to another vault first using: chamber registry switch <vault-name>");
println!("💡 Or list all vaults with: chamber registry list");
return Ok(());
}
if vault_count == 1 {
println!("⚠️ You are deleting the only remaining vault!");
}
print!("Are you sure you want to delete vault '{vault_id}'? ");
if delete_file {
print!("This will also delete the vault file! ");
}
print!("(y/N): ");
std::io::Write::flush(&mut std::io::stdout())?;
let mut input = String::new();
std::io::stdin().read_line(&mut input)?;
if input.trim().to_lowercase() == "y" {
manager.delete_vault(&vault_id, delete_file)?;
println!("✅ Deleted vault: {vault_id}");
} else {
println!("Cancelled");
}
}
crate::VaultCommand::Import {
file,
name,
category,
copy,
} => {
let category = parse_category(&category);
let vault_id = manager.import_vault(&file, name.clone(), category, copy)?;
println!("Imported vault '{name}' with ID: {vault_id}");
}
crate::VaultCommand::Info { vault } => {
let vault_id = if let Some(v) = vault {
find_vault_id(manager, &v)?
} else {
manager
.registry
.get_active_vault()
.ok_or_else(|| eyre!("No active vault"))?
.id
.clone()
};
if let Some(info) = manager.registry.get_vault(&vault_id) {
println!("Vault Information:");
println!(" ID: {}", info.id);
println!(" Name: {}", info.name);
println!(" Category: {}", info.category);
println!(" Path: {}", info.path.display());
println!(
" Created: {}",
info.created_at.format(&time::format_description::well_known::Rfc3339)?
);
println!(
" Last accessed: {}",
info.last_accessed
.format(&time::format_description::well_known::Rfc3339)?
);
println!(" Active: {}", info.is_active);
println!(" Favorite: {}", info.is_favorite);
if let Some(desc) = &info.description {
println!(" Description: {desc}");
}
}
}
crate::VaultCommand::Update {
vault,
name,
description,
category,
favorite,
} => {
let vault_id = find_vault_id(manager, &vault)?;
let category = category.map(|cat| parse_category(&cat));
manager.update_vault_info(&vault_id, name, description, category, favorite)?;
println!("Updated vault: {vault_id}");
}
}
Ok(())
}
fn parse_category(category: &str) -> VaultCategory {
match category.to_lowercase().as_str() {
"personal" => VaultCategory::Personal,
"work" => VaultCategory::Work,
"team" => VaultCategory::Team,
"project" => VaultCategory::Project,
"testing" => VaultCategory::Testing,
"archive" => VaultCategory::Archive,
custom => VaultCategory::Custom(custom.to_string()),
}
}
fn find_vault_id(manager: &VaultManager, identifier: &str) -> color_eyre::Result<String> {
if manager.registry.vaults.contains_key(identifier) {
return Ok(identifier.to_string());
}
for (id, vault_info) in &manager.registry.vaults {
if vault_info.name.to_lowercase() == identifier.to_lowercase() {
return Ok(id.clone());
}
}
Err(eyre!("Vault '{}' not found", identifier))
}