#[cfg(not(feature = "cli"))]
compile_error!("The `cli` feature must be enabled to build the sheets-diff binary.");
use std::process;
use clap::{Parser, ValueEnum};
use sheets_diff::{
DiffOptions, OpenErrorKind, ReadErrorKind, SheetsDiffError,
output::text::{render_summary, render_unified},
};
#[derive(Parser)]
#[command(
name = "sheets-diff",
about = "Structured diff engine for Excel .xlsx workbooks",
after_help = "EXIT CODES:\n \
0 no differences found\n \
1 differences found\n \
2 operational error (invalid options, a resource limit was \
hit, an environment issue such as a missing or unreadable \
file, or an internal bug)\n \
3 invalid or corrupt input (the file at the given path is \
not a readable .xlsx workbook: wrong format, corrupt \
internals, or encrypted)",
version
)]
struct Cli {
old: std::path::PathBuf,
new: std::path::PathBuf,
#[arg(short, long, value_enum, default_value_t = OutputFormat::Summary)]
format: OutputFormat,
#[arg(long)]
no_formulas: bool,
#[arg(long)]
no_warnings: bool,
}
#[derive(Copy, Clone, PartialEq, Eq, ValueEnum)]
enum OutputFormat {
Summary,
Unified,
}
fn exit_code_for(err: &SheetsDiffError) -> i32 {
match err {
SheetsDiffError::OpenWorkbook { kind, .. } => match kind {
OpenErrorKind::NotXlsx | OpenErrorKind::Corrupt => 3,
OpenErrorKind::NotFound | OpenErrorKind::PermissionDenied | OpenErrorKind::Locked => 2,
_ => 2,
},
SheetsDiffError::ReadSheet { kind, .. } => match kind {
ReadErrorKind::SheetNotFound | ReadErrorKind::MalformedSheet => 3,
ReadErrorKind::Other => 2,
_ => 2,
},
SheetsDiffError::UnsupportedFormat { .. } => 3,
SheetsDiffError::EncryptedWorkbook { .. } => 3,
SheetsDiffError::InvalidOptions { .. } => 2,
SheetsDiffError::Cancelled => 2,
SheetsDiffError::LimitExceeded { .. } => 2,
SheetsDiffError::Internal { .. } => 2,
_ => 2,
}
}
fn main() {
let cli = Cli::parse();
let mut builder = DiffOptions::builder();
if cli.no_formulas {
builder = builder.formula_compare(sheets_diff::FormulaCompareMode::Ignore);
}
let opts = match builder.build() {
Ok(o) => o,
Err(e) => {
eprintln!("sheets-diff: invalid options: {e}");
process::exit(2);
}
};
match sheets_diff::compare_paths_with_options(&cli.old, &cli.new, opts) {
Ok(diff) => {
let output = match cli.format {
OutputFormat::Summary => render_summary(&diff),
OutputFormat::Unified => render_unified(&diff),
};
print!("{output}");
if diff.summary.cells_changed > 0
|| diff.summary.sheets_added > 0
|| diff.summary.sheets_removed > 0
|| diff.summary.sheets_renamed > 0
{
process::exit(1);
}
}
Err(e) => {
eprintln!("sheets-diff: {e}");
if let Some(src) = std::error::Error::source(&e) {
eprintln!(" caused by: {src}");
}
process::exit(exit_code_for(&e));
}
}
}