refactor(cli): move cmd_get/list/history/status/sync into commands/

This commit is contained in:
adlee-was-taken
2026-05-06 18:43:41 -04:00
parent 13c2fc2bd7
commit da7d7d162c
6 changed files with 286 additions and 268 deletions

View File

@@ -0,0 +1,103 @@
//! `relicario list` and `relicario history` — both read-only browse paths.
use anyhow::Result;
pub fn cmd_list(
type_filter: Option<String>,
group_filter: Option<String>,
tag_filter: Option<String>,
trashed: bool,
) -> Result<()> {
use relicario_core::ItemType;
let vault = crate::session::UnlockedVault::unlock_interactive()?;
let manifest = vault.load_manifest()?;
crate::refresh_groups_cache(vault.root(), &manifest);
let parsed_type: Option<ItemType> = match type_filter.as_deref() {
None => None,
Some("login") => Some(ItemType::Login),
Some("secure_note") | Some("note") => Some(ItemType::SecureNote),
Some("identity") => Some(ItemType::Identity),
Some("card") => Some(ItemType::Card),
Some("key") => Some(ItemType::Key),
Some("document") => Some(ItemType::Document),
Some("totp") => Some(ItemType::Totp),
Some(other) => anyhow::bail!("unknown type filter: {other}"),
};
let mut entries: Vec<_> = manifest.items.values()
.filter(|e| {
if trashed { e.trashed_at.is_some() } else { e.trashed_at.is_none() }
})
.filter(|e| match parsed_type {
Some(t) => e.r#type == t,
None => true,
})
.filter(|e| group_filter.as_ref().is_none_or(|g| e.group.as_deref() == Some(g.as_str())))
.filter(|e| tag_filter.as_ref().is_none_or(|t| e.tags.iter().any(|x| x == t)))
.collect();
entries.sort_by(|a, b| a.title.to_lowercase().cmp(&b.title.to_lowercase()));
if entries.is_empty() {
eprintln!("(no items match)");
return Ok(());
}
println!("{:<16} {:<14} {:<6} TITLE", "ID", "TYPE", "FAV");
for e in entries {
let fav = if e.favorite { " *" } else { "" };
println!("{:<16} {:<14} {:<6} {}", e.id.as_str(), format!("{:?}", e.r#type), fav, e.title);
}
Ok(())
}
pub fn cmd_history(query: String, show: bool, field: Option<String>) -> Result<()> {
let vault = crate::session::UnlockedVault::unlock_interactive()?;
let manifest = vault.load_manifest()?;
let entry = super::resolve_query(&manifest, &query)?;
let item = vault.load_item(&entry.id)?;
println!("History for {} ({})", item.title, item.id.as_str());
println!();
// Filter and sort the field-id keys so output is deterministic.
let mut keys: Vec<&relicario_core::FieldId> = item.field_history.keys().collect();
keys.sort_by(|a, b| a.0.cmp(&b.0));
let mut printed_any = false;
for fid in keys {
let display_name = fid.0.strip_prefix("core:").unwrap_or(&fid.0);
if let Some(filter) = &field {
if display_name != filter && fid.0 != *filter { continue; }
}
let entries = &item.field_history[fid];
if entries.is_empty() { continue; }
printed_any = true;
println!("{display_name} ({} {})",
entries.len(),
if entries.len() == 1 { "entry" } else { "entries" });
for (i, e) in entries.iter().enumerate() {
let ts = crate::helpers::iso8601(e.replaced_at);
if show {
println!(" [{i}] {ts} {}", e.value.as_str());
} else {
println!(" [{i}] {ts} ********");
}
}
println!();
}
if !printed_any {
if field.is_some() {
println!("no history for the requested field");
} else {
println!("no history captured for this item");
}
} else if !show {
println!("(use --show to reveal values)");
}
Ok(())
}