inferlab-runtime 0.8.0

Framework-neutral runtime lifecycle mechanics for the InferLab product.
Documentation
use std::sync::Arc;
use std::sync::OnceLock;
use std::sync::atomic::{AtomicBool, Ordering};

static INTERRUPTED: AtomicBool = AtomicBool::new(false);
static CTRL_C_HANDLER: OnceLock<Result<(), InterruptInstallError>> = OnceLock::new();

#[derive(Clone, Debug, thiserror::Error)]
pub enum InterruptInstallError {
    #[error(
        "failed to install Ctrl-C handler: Ctrl-C error: Signal could not be found from the system ({signal})"
    )]
    NoSuchSignal { signal: String },
    #[error(
        "failed to install Ctrl-C handler: Ctrl-C error: Ctrl-C signal handler already registered"
    )]
    MultipleHandlers,
    #[error("failed to install Ctrl-C handler: Ctrl-C error: Unexpected system error")]
    System {
        #[source]
        source: Arc<std::io::Error>,
    },
}

impl From<ctrlc::Error> for InterruptInstallError {
    fn from(source: ctrlc::Error) -> Self {
        match source {
            ctrlc::Error::NoSuchSignal(signal) => Self::NoSuchSignal {
                signal: format!("{signal:?}"),
            },
            ctrlc::Error::MultipleHandlers => Self::MultipleHandlers,
            ctrlc::Error::System(source) => Self::System {
                source: Arc::new(source),
            },
        }
    }
}

pub fn prepare() -> Result<(), InterruptInstallError> {
    INTERRUPTED.store(false, Ordering::SeqCst);
    CTRL_C_HANDLER
        .get_or_init(|| {
            ctrlc::set_handler(|| INTERRUPTED.store(true, Ordering::SeqCst))
                .map_err(InterruptInstallError::from)
        })
        .clone()
}

pub fn received() -> bool {
    INTERRUPTED.load(Ordering::SeqCst)
}

#[cfg(test)]
mod tests {
    use super::InterruptInstallError;

    #[test]
    fn ctrlc_categories_remain_typed() {
        let missing =
            InterruptInstallError::from(ctrlc::Error::NoSuchSignal(ctrlc::SignalType::Ctrlc));
        assert!(matches!(
            missing,
            InterruptInstallError::NoSuchSignal { .. }
        ));

        let duplicate = InterruptInstallError::from(ctrlc::Error::MultipleHandlers);
        assert!(matches!(duplicate, InterruptInstallError::MultipleHandlers));

        let system = InterruptInstallError::from(ctrlc::Error::System(std::io::Error::other(
            "handler installation failed",
        )));
        assert!(matches!(system, InterruptInstallError::System { .. }));
    }
}