refactor(xatu): separate CLI execution from error reporting

This commit is contained in:
2026-09-24 23:01:48 -04:00
parent 7b96309045
commit c251500a70
2 changed files with 43 additions and 49 deletions

View File

@@ -124,3 +124,5 @@
Copy Nix fixtures into `TEST_TMPDIR` before passing their paths to CLI tests. Copy Nix fixtures into `TEST_TMPDIR` before passing their paths to CLI tests.
- The vendored `rustversion-1.0.23/build/` files are currently ignored and untracked. - The vendored `rustversion-1.0.23/build/` files are currently ignored and untracked.
Fresh worktrees cannot build Xatu tests until those upstream files are restored. Fresh worktrees cannot build Xatu tests until those upstream files are restored.
- `status::ResultExt` requires errors that implement `Into<Status>`, not arbitrary `std::error::Error` values.
Map other errors explicitly with a status code and `with_source`.

View File

@@ -5,45 +5,46 @@ use std::process::ExitCode;
use xatu::SourceFile; use xatu::SourceFile;
fn main() -> ExitCode { fn main() -> ExitCode {
let args = Args::parse(); match run(Args::parse()) {
Ok(exit_code) => exit_code,
Err(error) => {
eprintln!("{error}");
ExitCode::from(2)
}
}
}
let debug = args.debug; fn run(args: Args) -> status::Result<ExitCode> {
let files = match collect_files(args) { let files = collect_files(args.paths)?;
Ok(files) => files, if args.debug {
Err(error) => { dump_parsed_files(files)
eprintln!("{error}"); } else {
return ExitCode::from(2); check(files)
} }
}; }
if debug {
fn dump_parsed_files(files: Vec<SourceFile>) -> status::Result<ExitCode> {
let report = xatu::debug(files); let report = xatu::debug(files);
match serde_json::to_string_pretty(&report) { let json = serde_json::to_string_pretty(&report).map_err(|error| {
Ok(json) => println!("{json}"), status::Status::internal("failed to serialize parser output").with_source(error)
Err(error) => { })?;
eprintln!("{error}"); println!("{json}");
return ExitCode::from(2);
} Ok(if report.has_parse_errors() {
}
return if report.has_parse_errors() {
ExitCode::from(2) ExitCode::from(2)
} else { } else {
ExitCode::SUCCESS ExitCode::SUCCESS
}; })
} }
match xatu::future_sight(files) { fn check(files: Vec<SourceFile>) -> status::Result<ExitCode> {
Ok(report) if report.is_clean() => { let report = xatu::future_sight(files)?;
if report.is_clean() {
println!("No findings."); println!("No findings.");
ExitCode::SUCCESS Ok(ExitCode::SUCCESS)
} } else {
Ok(report) => {
print!("{report}"); print!("{report}");
ExitCode::from(1) Ok(ExitCode::from(1))
}
Err(error) => {
eprintln!("{error}");
ExitCode::from(2)
}
} }
} }
@@ -56,21 +57,12 @@ struct Args {
paths: Vec<PathBuf>, paths: Vec<PathBuf>,
} }
fn collect_files(args: Args) -> status::Result<Vec<SourceFile>> { fn collect_files(paths: Vec<PathBuf>) -> status::Result<Vec<SourceFile>> {
let paths: Vec<PathBuf> = args let mut files = Vec::new();
.paths for path in paths {
.into_iter() let path = monorepo::workspace::resolve_workspace_path(path);
.map(monorepo::workspace::resolve_workspace_path) files.extend(collect_source_files(&path)?);
.collect(); }
let files: Vec<SourceFile> = paths
.iter()
.map(|path| collect_source_files(path))
.collect::<status::Result<Vec<_>>>()?
.into_iter()
.flatten()
.collect();
Ok(files) Ok(files)
} }