///|
/// CLI entry point.
///
/// The executable keeps `main` intentionally thin: it normalizes argv,
/// delegates all parsing and checking to library code, then converts the final
/// `CliOutcome` into stdout output plus a process exit code.
async fn main {
let args = @env.args()
let user_args : Array[String] = []
let mut i = 1
while i < args.length() {
// `@env.args()` includes argv[0], but the CLI parser expects only
// user-provided flags and filenames.
user_args.push(args[i])
i += 1
}
let outcome = match parse_cli_args(user_args) {
Ok(config) =>
// Successful parsing only produces a normalized config; all IO and
// checking happens in the higher-level driver so the parser stays pure.
@driver.run_cli(
config.action,
config.files,
config.spec,
config.format,
config.format_options,
)
Err(error) =>
// Usage and support errors are reported through the same `CliOutcome`
// shape as normal runs so `main` only needs one rendering path.
@driver.CliOutcome::new(error.message, cli_exit_code(error.kind))
}
if outcome.output != "" {
println(outcome.output)
}
if outcome.exit_code != 0 {
// Returning normally would report success, so non-zero outcomes must exit
// explicitly after rendering their message.
exit_process(outcome.exit_code)
}
}