mod commands;
mod logging;
mod output;
use std::path::PathBuf;
use clap::{Parser, Subcommand};
use sdivi_core::ExitCode;
use commands::boundaries::BoundariesSubcmd;
#[derive(Parser)]
#[command(name = "sdivi", version, about, long_about = None)]
struct Cli {
#[arg(long, default_value = ".")]
repo: PathBuf,
#[command(subcommand)]
command: Option<Commands>,
}
#[derive(Subcommand)]
enum Commands {
Init,
Catalog {
#[arg(long, default_value = "text")]
format: String,
},
Snapshot {
#[arg(long)]
commit: Option<String>,
#[arg(long, default_value = "text")]
format: String,
},
Diff {
prev: PathBuf,
curr: PathBuf,
#[arg(long, default_value = "text")]
format: String,
},
Check {
#[arg(long)]
no_write: bool,
#[arg(long, default_value = "text")]
format: String,
},
Trend {
#[arg(long)]
last: Option<usize>,
#[arg(long, default_value = "text")]
format: String,
},
Show {
id: Option<String>,
#[arg(long, default_value = "text")]
format: String,
},
Boundaries {
#[command(subcommand)]
subcmd: BoundariesSubcmd,
},
}
pub fn run() {
let cli = Cli::parse();
logging::init();
let config = match sdivi_config::load_or_default(&cli.repo) {
Ok(c) => c,
Err(e) => {
eprintln!("sdivi: error: {e:#}");
std::process::exit(ExitCode::ConfigError.as_i32());
}
};
if let Some(Commands::Check { no_write, format }) = &cli.command {
match commands::check::run(&cli.repo, &config, *no_write, format) {
Ok(code) => std::process::exit(code.as_i32()),
Err(e) => {
eprintln!("sdivi: error: {e:#}");
std::process::exit(error_exit_code(&e).as_i32());
}
}
}
let result = match cli.command {
Some(Commands::Init) => commands::init::run(&cli.repo),
Some(Commands::Catalog { format }) => commands::catalog::run(&cli.repo, &config, &format),
Some(Commands::Snapshot { commit, format }) => {
commands::snapshot::run(&cli.repo, &config, commit.as_deref(), &format)
}
Some(Commands::Diff { prev, curr, format }) => commands::diff::run(&prev, &curr, &format),
Some(Commands::Check { .. }) => unreachable!("handled above"),
Some(Commands::Trend { last, format }) => {
commands::trend::run(&cli.repo, &config, last, &format)
}
Some(Commands::Show { id, format }) => {
commands::show::run(&cli.repo, &config, id.as_deref(), &format)
}
Some(Commands::Boundaries { subcmd }) => {
commands::boundaries::run(subcmd, &cli.repo, &config)
}
None => {
eprintln!("sdivi: no subcommand given — try `sdivi --help`");
return;
}
};
if let Err(e) = result {
let code = error_exit_code(&e);
eprintln!("sdivi: error: {e:#}");
std::process::exit(code.as_i32());
}
}
fn error_exit_code(e: &anyhow::Error) -> ExitCode {
if e.downcast_ref::<sdivi_config::ConfigError>().is_some() {
return ExitCode::ConfigError;
}
if let Some(pe) = e.downcast_ref::<sdivi_pipeline::PipelineError>() {
if matches!(pe, sdivi_pipeline::PipelineError::NoGrammarsAvailable) {
return ExitCode::AnalysisError;
}
}
ExitCode::RuntimeError
}