use std::io::{BufReader, stdin};
use std::path::PathBuf;
use std::process::ExitCode;
use clap::{Parser, Subcommand};
use eyre::{Context, Report};
use rustls_pki_types::CertificateDer;
use rustls_pki_types::pem::PemObject;
use tracing::level_filters::LevelFilter;
use tracing_subscriber::EnvFilter;
use tracing_subscriber::layer::SubscriberExt;
use tracing_subscriber::util::SubscriberInitExt;
use upki::revocation::{Index, Manifest, RevocationCheckInput, fetch};
use upki::{Config, ConfigPath};
#[tokio::main(flavor = "current_thread")]
async fn main() -> Result<ExitCode, Report> {
let args = Args::parse();
tracing_subscriber::registry()
.with(tracing_subscriber::fmt::layer().compact())
.with(
EnvFilter::builder()
.with_default_directive(
match args.verbose {
true => LevelFilter::TRACE,
false => LevelFilter::ERROR,
}
.into(),
)
.from_env()?,
)
.init();
let config_path =
ConfigPath::new(args.config_file).wrap_err("cannot find configuration path")?;
if let Command::ShowConfigPath = args.command {
println!("{}", config_path.as_ref().display());
return Ok(ExitCode::SUCCESS);
}
let config = Config::from_file_or_user_default(&config_path)?;
Ok(match args.command {
Command::Fetch { dry_run } => fetch(dry_run, &config).await?,
Command::Verify => Manifest::from_config(&config)?.verify(&config)?,
Command::ShowConfigPath => unreachable!(),
Command::ShowConfig => {
print!(
"{}",
toml::to_string_pretty(&config).wrap_err("cannot format configuration")?
);
ExitCode::SUCCESS
}
Command::Revocation(Revocation::Check) => {
let mut certs = vec![];
for cert in CertificateDer::pem_reader_iter(&mut BufReader::new(stdin())) {
certs.push(cert.wrap_err("cannot read certificate from stdin")?);
}
let input = RevocationCheckInput::from_certificates(&certs)?;
Index::from_cache(&config)?
.check(&input)?
.to_cli()
}
})
}
#[derive(Debug, Parser)]
#[command(name = "upki", author, version, about)]
struct Args {
#[command(subcommand)]
command: Command,
#[arg(long)]
config_file: Option<PathBuf>,
#[arg(long)]
verbose: bool,
}
#[derive(Debug, Subcommand)]
enum Command {
Fetch {
#[arg(long)]
dry_run: bool,
},
Verify,
#[clap(subcommand)]
Revocation(Revocation),
ShowConfigPath,
ShowConfig,
}
#[derive(Debug, Subcommand)]
enum Revocation {
Check,
}