vios_app 0.1.1

Small JSON vaults: Argon2id + AES-GCM, with optional AAD binding.
Documentation
// src/bin/pdf_exporter_main.rs
// ๐Ÿงพ Export a decrypted Vault to PDF
//
// Usage:
//   cargo run --locked --features pdf --bin pdf_exporter_main -- <VAULT> [--pdf-out out.pdf]
//
// Password options:
//   --password-from-env  (reads VIOS_PASSWORD)
//   --password-stdin     (reads from stdin; no prompt)

use std::error::Error;
use std::path::PathBuf;

use clap::Parser;

// Shared banner (ensure `pub mod build_info;` exists in src/lib.rs)
use vios_core::build_info;

#[cfg(feature = "pdf")]
use {
    std::io::{self, Read},
    std::path::Path,
    vios_core::vault::pdf_exporter::export_vault_to_pdf,
};

#[cfg(feature = "boot-cli")]
use vios_core::cli::boot::BootCli;

/// Export a VIOS vault to PDF
#[derive(Parser, Debug)]
#[command(
    name = "pdf_exporter_main",
    version,
    about = "Export a VIOS vault to PDF"
)]
struct Args {
    /// Boot console flags (compiled only when `boot-cli` is enabled)
    #[cfg(feature = "boot-cli")]
    #[command(flatten)]
    boot: BootCli,

    /// Path to .vault file
    #[arg(value_name = "VAULT")]
    vault: Option<PathBuf>,

    /// Output PDF path (default: <VAULT-stem>.pdf in the same dir)
    #[arg(long, value_name = "PDF_OUT")]
    pdf_out: Option<PathBuf>,

    /// Read password from env var VIOS_PASSWORD
    #[arg(long)]
    password_from_env: bool,

    /// Read password from stdin (no prompt)
    #[arg(long)]
    password_stdin: bool,
}

#[cfg(not(feature = "pdf"))]
fn main() -> Result<(), Box<dyn Error>> {
    build_info::print_banner();
    eprintln!("โŒ PDF feature is disabled. Rebuild with `--features pdf`.");
    std::process::exit(2);
}

#[cfg(feature = "pdf")]
fn main() -> Result<(), Box<dyn Error>> {
    build_info::print_banner();

    let args = Args::parse();

    #[cfg(feature = "boot-cli")]
    {
        // If BootCli exposes activation hooks, call them here (e.g., args.boot.apply()?).
        let _ = &args.boot;
    }

    let vault_path = args.vault.clone().ok_or("Missing <VAULT> argument")?;
    let out_path = args
        .pdf_out
        .clone()
        .unwrap_or_else(|| default_pdf_out_path(&vault_path));

    let password = get_password(&args)?;

    // Convert PathBuf โ†’ &str for the exporter
    let vault_str = vault_path.to_str().ok_or("Vault path is not valid UTF-8")?;
    let out_str = out_path.to_str().ok_or("Output path is not valid UTF-8")?;

    match export_vault_to_pdf(vault_str, password.trim(), out_str) {
        Ok(()) => {
            println!("โœ… Exported to {}", out_path.display());
            Ok(())
        }
        Err(e) => {
            eprintln!("โŒ Error: {e}");
            std::process::exit(1);
        }
    }
}

#[cfg(feature = "pdf")]
fn default_pdf_out_path(vault: &Path) -> PathBuf {
    let mut out = vault.to_path_buf();
    let stem = vault
        .file_stem()
        .and_then(|s| s.to_str())
        .unwrap_or("vault_export");
    out.set_file_name(format!("{stem}.pdf"));
    out
}

#[cfg(feature = "pdf")]
fn get_password(args: &Args) -> Result<String, Box<dyn Error>> {
    if args.password_from_env {
        let p = std::env::var("VIOS_PASSWORD")
            .map_err(|_| "VIOS_PASSWORD not set (use --password-from-env or omit the flag)")?;
        return Ok(p);
    }
    if args.password_stdin {
        let mut buf = String::new();
        io::stdin().read_to_string(&mut buf)?;
        return Ok(buf);
    }
    // Interactive, no-echo
    let p = rpassword::prompt_password("Enter password: ")?;
    Ok(p)
}