Skip to main content

bijux_cli/bootstrap/
run.rs

1#![forbid(unsafe_code)]
2//! CLI process entrypoint orchestration.
3
4use std::io::{self, Write};
5use std::process::ExitCode;
6
7use crate::bootstrap::repl::try_run_interactive_repl;
8use crate::bootstrap::wiring::{decode_os_argv, emit_run_result};
9use crate::interface::cli::dispatch::run_app;
10use crate::kernel::map_error_category_to_exit;
11use crate::shared::telemetry::TelemetrySpan;
12
13fn normalize_process_exit_code(code: i32) -> u8 {
14    if code <= 0 {
15        return u8::from(code != 0);
16    }
17    if code > i32::from(u8::MAX) {
18        return u8::MAX;
19    }
20    code as u8
21}
22
23/// Execute the CLI process using current OS argv.
24#[must_use]
25pub fn run_cli_from_env() -> ExitCode {
26    let argv = match decode_os_argv() {
27        Ok(value) => value,
28        Err(_) => {
29            let telemetry = TelemetrySpan::start(
30                "bijux-cli",
31                &["bijux".to_string(), "<invalid-utf8-argv>".to_string()],
32            );
33            telemetry.record(
34                "argv.decode.error",
35                serde_json::json!({"message":"invalid UTF-8 argument in argv"}),
36            );
37            telemetry.finish_exit(2, 0, "invalid UTF-8 argument in argv\n".len());
38            let _ = writeln!(io::stderr(), "invalid UTF-8 argument in argv");
39            return ExitCode::from(map_error_category_to_exit("usage") as u8);
40        }
41    };
42
43    match try_run_interactive_repl(&argv) {
44        Ok(Some(code)) => return ExitCode::from(normalize_process_exit_code(code)),
45        Ok(None) => {}
46        Err(error) => {
47            let _ = writeln!(io::stderr(), "{error}");
48            return ExitCode::from(1);
49        }
50    }
51
52    let result = match run_app(&argv) {
53        Ok(value) => value,
54        Err(error) => {
55            let _ = writeln!(io::stderr(), "{error}");
56            return ExitCode::from(1);
57        }
58    };
59
60    emit_run_result(&result);
61    ExitCode::from(normalize_process_exit_code(result.exit_code))
62}
63
64#[cfg(test)]
65mod tests {
66    use super::normalize_process_exit_code;
67
68    #[test]
69    fn normalize_exit_code_clamps_negative_and_large_values() {
70        assert_eq!(normalize_process_exit_code(0), 0);
71        assert_eq!(normalize_process_exit_code(2), 2);
72        assert_eq!(normalize_process_exit_code(-1), 1);
73        assert_eq!(normalize_process_exit_code(300), u8::MAX);
74    }
75}