//! # mcptest_cli
//!
//! Library for the `mcptest` CLI binary. The `main.rs` is a thin shell that
//! delegates to [`run`]. Putting the entry point in a library makes the
//! parser testable from integration tests (`assert_cmd` + direct
//! `Cli::parse_from`) without invoking a subprocess for every test.

#![deny(missing_docs)]
#![deny(rustdoc::broken_intra_doc_links)]

pub mod cli;
pub mod commands;
pub mod config;
pub mod doctor;
pub mod errfmt;
pub mod logging;
#[cfg(feature = "otlp")]
pub mod otel_export;
pub mod pipe;
pub mod scorers;
pub mod server_override;
pub mod watch;

pub use cli::Cli;

/// Run the CLI as if invoked from the shell.
///
/// `args` should include the program name in slot 0 (matches `std::env::args`).
/// Returns the exit code the binary should hand back to the OS. Errors are
/// rendered to stderr inside this function so the caller does not need to
/// re-render `anyhow::Error`.
pub fn run<I, T>(args: I) -> i32
where
    I: IntoIterator<Item = T>,
    T: Into<std::ffi::OsString> + Clone,
{
    // Buffer the args so we can peek for logging flags before clap runs.
    // Slot 0 is the program name (matches `std::env::args`), so we keep it
    // untouched and only inspect the rest.
    let collected: Vec<std::ffi::OsString> = args.into_iter().map(|a| a.into()).collect();
    let logging_args = logging::peek_logging_args(&collected);
    // Held until the process exits so an OTLP exporter (when built with the
    // `otlp` feature and `--otel-endpoint`) flushes its batched spans on the
    // way out. A no-op in the default build.
    let _log_guard = match logging::init(&logging_args) {
        Ok(guard) => guard,
        Err(err) => {
            eprintln!("mcptest: {}", errfmt::render_error_chain(&err));
            return 1;
        }
    };

    match cli::dispatch(collected) {
        Ok(code) => code,
        Err(err) => {
            eprintln!("mcptest: {}", errfmt::render_error_chain(&err));
            1
        }
    }
}
