#![cfg_attr(not(feature = "pdf"), allow(unused_variables))]
use std::env;
use std::error::Error;
use std::fmt::{self, Display};
use std::io::{self, Read, Write};
use clap::Parser;
use rpassword::read_password;
use vios_core::vault::vault_crypto::decrypt_vault;
#[derive(Parser, Debug)]
#[command(
name = "vaultviewer",
about = "View a VIOS vault (and optionally export to PDF)",
version
)]
struct Args {
path: String,
#[arg(long)]
pdf_out: Option<String>,
#[arg(long)]
password_from_env: bool,
#[arg(long)]
password_stdin: bool,
#[arg(long)]
dump_plaintext: bool,
}
#[derive(Debug)]
struct Msg(&'static str);
impl Display for Msg {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.0)
}
}
impl Error for Msg {}
fn read_password_interactive() -> Result<String, Box<dyn Error>> {
print!("Enter password: ");
io::stdout().flush().ok();
let pw = read_password().map_err(|_| Msg("failed to read password"))?;
Ok(pw)
}
fn read_password_from_stdin() -> Result<String, Box<dyn Error>> {
let mut buf = String::new();
io::stdin().read_to_string(&mut buf)?;
Ok(buf.trim_end().to_string())
}
fn resolve_password(args: &Args) -> Result<String, Box<dyn Error>> {
if args.password_from_env && args.password_stdin {
return Err(Box::new(Msg(
"use only one of --password-from-env or --password-stdin",
)));
}
if args.password_from_env {
Ok(env::var("VIOS_PASSWORD").map_err(|_| Msg("VIOS_PASSWORD not set"))?)
} else if args.password_stdin {
read_password_from_stdin()
} else {
read_password_interactive()
}
}
fn main() -> Result<(), Box<dyn Error>> {
let args = Args::parse();
let password = resolve_password(&args)?;
let payload = decrypt_vault(&args.path, &password)
.map_err(|e| format!("failed to decrypt '{}': {e}", &args.path))?;
if args.dump_plaintext {
println!("{}", payload.content);
} else {
println!("✅ Decrypted");
println!("creator : {}", payload.metadata.creator_name);
println!("created : {}", payload.metadata.created_at);
println!("content : {}", payload.content);
}
if let Some(out) = args.pdf_out.as_deref() {
#[cfg(feature = "pdf")]
{
vios_core::vault::pdf_exporter::export_vault_to_pdf(&args.path, &password, out)
.map_err(|e| format!("PDF export failed to '{}': {e}", out))?;
eprintln!("📝 PDF written: {out}");
}
#[cfg(not(feature = "pdf"))]
{
return Err(Box::new(Msg(
"pdf feature not enabled. Re-run with `--features pdf`.",
)));
}
}
Ok(())
}