use clap::Parser;
use sexpfmt::{PrinterConfig, SexpfmtError};
use std::fs::File;
use std::io;
use std::path::{Path, PathBuf};
use std::process::ExitCode;
#[derive(Parser)]
#[command(version)]
struct Cli {
files: Vec<PathBuf>,
#[arg(long, default_value_t = 2)]
indent: usize,
#[arg(long, default_value_t = 80)]
margin: usize,
}
fn main() -> ExitCode {
let cli = Cli::parse();
let config = PrinterConfig {
indent_width: cli.indent,
margin_width: cli.margin,
};
let stdout = io::stdout().lock();
let mut out = io::BufWriter::new(stdout);
let inputs = if cli.files.is_empty() {
vec![PathBuf::from("-")]
} else {
cli.files
};
for path in &inputs {
let result = if path.as_os_str() == "-" {
sexpfmt::format(io::stdin().lock(), &mut out, &config)
} else {
File::open(path)
.map_err(SexpfmtError::from)
.and_then(|file| sexpfmt::format(file, &mut out, &config))
};
match result {
Ok(()) => {}
Err(e) if is_broken_pipe(&e) => return ExitCode::SUCCESS,
Err(e) => {
report_error(path, &e);
return ExitCode::FAILURE;
}
}
}
ExitCode::SUCCESS
}
fn is_broken_pipe(e: &SexpfmtError) -> bool {
matches!(e, SexpfmtError::Io { source } if source.kind() == io::ErrorKind::BrokenPipe)
}
fn report_error(path: &Path, e: &SexpfmtError) {
if path.as_os_str() == "-" {
eprintln!("ERROR: {e}");
} else {
eprintln!("ERROR: {}: {e}", path.display());
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_cli_definition() {
use clap::CommandFactory;
Cli::command().debug_assert();
}
}