#![deny(missing_docs)]
#![deny(clippy::missing_docs_in_private_items)]
// SPDX-FileCopyrightText: Peter Pentchev <roam@ringlet.net>
// SPDX-License-Identifier: BSD-2-Clause
//! Execute a program, send a 'stop' signal after some time, send a 'kill' signal later.

#![doc(html_root_url = "https://docs.rs/timelimit/1.9.5")]
#![expect(clippy::pub_use, reason = "re-export common symbols")]

pub mod defs;
pub mod exitcode;
pub mod limit_smol;

pub use defs::{Config, FEATURES, Signal};
pub use exitcode::ExitCodeReport;

use std::ffi::OsStr;

use anyhow::anyhow;
use smol::process::Command as SmolCommand;

use limit_smol::Limit;

/// Run a command and limit its execution time.
///
/// Use the preferred runtime (currently only `smol`).
///
/// # Errors
///
/// Propagate errors from the runtime-specific handler: usually
/// [`sysexits::ExitCode::OsErr`] if the command could not be executed or
/// its exit status could not be determined.
#[inline]
pub async fn limit<S>(cfg: Config, command: &[S]) -> ExitCodeReport
where
    S: AsRef<OsStr> + Sync + Send,
{
    // Add more once we support other runtimes.
    let cmd = {
        let Some((prog, args)) = command.split_first() else {
            return anyhow!("timelimit internal error: no program to run").into();
        };
        let mut cmd = SmolCommand::new(prog);
        cmd.args(args);
        cmd
    };
    Limit::new(cfg).run(cmd).await
}

/// List the signals supported by the underlying implementation.
#[inline]
pub fn list_signals() -> impl Iterator<Item = &'static str> {
    Signal::list()
}
