use anyhow::{Context as _, Result};
use colored::Colorize;
use tsafe_cli::tsafe_keepass::{pull_entries, KeePassConfig, KeePassError};
use tsafe_core::pullconfig::PullSource;
use tsafe_core::{audit::AuditEntry, events::emit_event};
use crate::helpers::*;
use tsafe_cli::cli::PullOnError;
pub(crate) fn cmd_keepass_pull(
profile: &str,
src: &PullSource,
overwrite: bool,
on_error: PullOnError,
) -> Result<()> {
cmd_keepass_pull_ns(profile, src, overwrite, on_error, None)
}
pub(crate) fn cmd_keepass_pull_ns(
profile: &str,
src: &PullSource,
overwrite: bool,
on_error: PullOnError,
ns: Option<&str>,
) -> Result<()> {
let cfg = match KeePassConfig::from_pull_source(src) {
Ok(c) => c,
Err(KeePassError::PasswordRequired(env_var)) => {
return Err(anyhow::anyhow!(
"KeePass password is required but env var '{env_var}' is not set\n\
\n Fix: export {env_var}=<master-password> (or configure keyfile_path)",
));
}
Err(e) => {
return Err(anyhow::anyhow!("{e}").context("failed to build KeePass config"));
}
};
let entries = match pull_entries(&cfg)
.with_context(|| format!("failed to read KeePass database '{}'", cfg.path))
{
Ok(e) => e,
Err(err) => match on_error {
PullOnError::FailAll => return Err(err),
PullOnError::SkipFailed | PullOnError::WarnOnly => {
eprintln!("{} KeePass pull failed: {err}", "!".yellow());
return Ok(());
}
},
};
if entries.is_empty() {
println!(
"{} No entries found in the KeePass database matching the filter",
"i".blue()
);
return Ok(());
}
let mut vault = open_vault(profile)?;
let mut imported = 0usize;
let mut skipped = 0usize;
for (raw_key, value) in &entries {
let key = match ns {
Some(prefix) => format!("{prefix}.{raw_key}"),
None => raw_key.clone(),
};
let exists = vault.list().contains(&key.as_str());
if exists && !overwrite {
skipped += 1;
continue;
}
vault
.set(&key, value, std::collections::HashMap::new())
.with_context(|| format!("failed to store key '{key}' in vault"))?;
imported += 1;
}
audit(profile)
.append(&AuditEntry::success(profile, "kp-pull", None))
.ok();
emit_event(profile, "kp-pull", None);
println!(
"{} Imported {imported} secret(s) from KeePass database '{}'{}",
"✓".green(),
cfg.path,
if skipped > 0 {
format!(" ({skipped} skipped — use --overwrite to replace)")
} else {
String::new()
}
);
Ok(())
}