asupersync 0.3.0

Spec-first, cancel-correct, capability-secure async runtime for Rust.
Documentation
//! Signal kind enumeration for Unix signals.
//!
//! Provides a cross-platform representation of Unix signals.

/// Unix signal kinds.
///
/// This enum represents the various Unix signals that can be handled
/// asynchronously. On Windows, only a subset of signals are supported.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum SignalKind {
    /// SIGINT - Interrupt from keyboard (Ctrl+C).
    Interrupt,
    /// SIGTERM - Termination signal.
    Terminate,
    /// SIGHUP - Hangup detected on controlling terminal.
    Hangup,
    /// SIGQUIT - Quit from keyboard.
    Quit,
    /// SIGUSR1 - User-defined signal 1.
    User1,
    /// SIGUSR2 - User-defined signal 2.
    User2,
    /// SIGCHLD - Child stopped or terminated.
    Child,
    /// SIGWINCH - Window resize signal.
    WindowChange,
    /// SIGPIPE - Broken pipe.
    Pipe,
    /// SIGALRM - Timer signal.
    Alarm,
}

impl SignalKind {
    /// Creates a `SignalKind` for SIGINT (Ctrl+C).
    #[must_use]
    #[inline]
    pub const fn interrupt() -> Self {
        Self::Interrupt
    }

    /// Creates a `SignalKind` for SIGTERM.
    #[must_use]
    #[inline]
    pub const fn terminate() -> Self {
        Self::Terminate
    }

    /// Creates a `SignalKind` for SIGHUP.
    #[must_use]
    #[inline]
    pub const fn hangup() -> Self {
        Self::Hangup
    }

    /// Creates a `SignalKind` for SIGQUIT.
    #[must_use]
    #[inline]
    pub const fn quit() -> Self {
        Self::Quit
    }

    /// Creates a `SignalKind` for SIGUSR1.
    #[must_use]
    #[inline]
    pub const fn user_defined1() -> Self {
        Self::User1
    }

    /// Creates a `SignalKind` for SIGUSR2.
    #[must_use]
    #[inline]
    pub const fn user_defined2() -> Self {
        Self::User2
    }

    /// Creates a `SignalKind` for SIGCHLD.
    #[must_use]
    #[inline]
    pub const fn child() -> Self {
        Self::Child
    }

    /// Creates a `SignalKind` for SIGWINCH.
    #[must_use]
    #[inline]
    pub const fn window_change() -> Self {
        Self::WindowChange
    }

    /// Creates a `SignalKind` for SIGPIPE.
    #[must_use]
    #[inline]
    pub const fn pipe() -> Self {
        Self::Pipe
    }

    /// Creates a `SignalKind` for SIGALRM.
    #[must_use]
    #[inline]
    pub const fn alarm() -> Self {
        Self::Alarm
    }

    /// Returns the platform signal number on Unix.
    #[cfg(unix)]
    #[must_use]
    #[inline]
    pub const fn as_raw_value(&self) -> i32 {
        match self {
            Self::Interrupt => libc::SIGINT,
            Self::Terminate => libc::SIGTERM,
            Self::Hangup => libc::SIGHUP,
            Self::Quit => libc::SIGQUIT,
            Self::User1 => libc::SIGUSR1,
            Self::User2 => libc::SIGUSR2,
            Self::Child => libc::SIGCHLD,
            Self::WindowChange => libc::SIGWINCH,
            Self::Pipe => libc::SIGPIPE,
            Self::Alarm => libc::SIGALRM,
        }
    }

    /// Returns the signal number on Windows platforms.
    ///
    /// Supported mappings:
    /// - `Interrupt` -> `SIGINT`
    /// - `Terminate` -> `SIGTERM`
    /// - `Quit` -> `SIGBREAK`
    ///
    /// Other signal kinds are unsupported and return `None`.
    #[cfg(windows)]
    #[must_use]
    #[inline]
    pub const fn as_raw_value(&self) -> Option<i32> {
        // signal_hook::consts::SIGBREAK is 21 on Windows (Ctrl+Break).
        // We use the literal here because `const fn` cannot call non-const
        // items from external crates.
        const SIGBREAK: i32 = 21;
        match self {
            Self::Interrupt => Some(libc::SIGINT),
            Self::Terminate => Some(libc::SIGTERM),
            Self::Quit => Some(SIGBREAK),
            _ => None,
        }
    }

    /// Returns the signal number on other non-Unix platforms.
    ///
    /// Returns `None` when no mapping exists.
    #[cfg(not(any(unix, windows)))]
    #[must_use]
    #[inline]
    pub const fn as_raw_value(&self) -> Option<i32> {
        None
    }

    /// Returns the name of the signal.
    #[must_use]
    #[inline]
    pub const fn name(&self) -> &'static str {
        match self {
            Self::Interrupt => "SIGINT",
            Self::Terminate => "SIGTERM",
            Self::Hangup => "SIGHUP",
            Self::Quit => "SIGQUIT",
            Self::User1 => "SIGUSR1",
            Self::User2 => "SIGUSR2",
            Self::Child => "SIGCHLD",
            Self::WindowChange => "SIGWINCH",
            Self::Pipe => "SIGPIPE",
            Self::Alarm => "SIGALRM",
        }
    }
}

impl std::fmt::Display for SignalKind {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "{}", self.name())
    }
}

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

    fn init_test(name: &str) {
        crate::test_utils::init_test_logging();
        crate::test_phase!(name);
    }

    #[test]
    fn signal_kind_constructors() {
        init_test("signal_kind_constructors");
        crate::assert_with_log!(
            SignalKind::interrupt() == SignalKind::Interrupt,
            "interrupt",
            SignalKind::Interrupt,
            SignalKind::interrupt()
        );
        crate::assert_with_log!(
            SignalKind::terminate() == SignalKind::Terminate,
            "terminate",
            SignalKind::Terminate,
            SignalKind::terminate()
        );
        crate::assert_with_log!(
            SignalKind::hangup() == SignalKind::Hangup,
            "hangup",
            SignalKind::Hangup,
            SignalKind::hangup()
        );
        crate::assert_with_log!(
            SignalKind::quit() == SignalKind::Quit,
            "quit",
            SignalKind::Quit,
            SignalKind::quit()
        );
        crate::assert_with_log!(
            SignalKind::user_defined1() == SignalKind::User1,
            "user1",
            SignalKind::User1,
            SignalKind::user_defined1()
        );
        crate::assert_with_log!(
            SignalKind::user_defined2() == SignalKind::User2,
            "user2",
            SignalKind::User2,
            SignalKind::user_defined2()
        );
        crate::assert_with_log!(
            SignalKind::child() == SignalKind::Child,
            "child",
            SignalKind::Child,
            SignalKind::child()
        );
        crate::assert_with_log!(
            SignalKind::window_change() == SignalKind::WindowChange,
            "window_change",
            SignalKind::WindowChange,
            SignalKind::window_change()
        );
        crate::test_complete!("signal_kind_constructors");
    }

    #[test]
    fn signal_kind_names() {
        init_test("signal_kind_names");
        let interrupt = SignalKind::Interrupt.name();
        crate::assert_with_log!(interrupt == "SIGINT", "interrupt", "SIGINT", interrupt);
        let terminate = SignalKind::Terminate.name();
        crate::assert_with_log!(terminate == "SIGTERM", "terminate", "SIGTERM", terminate);
        let hangup = SignalKind::Hangup.name();
        crate::assert_with_log!(hangup == "SIGHUP", "hangup", "SIGHUP", hangup);
        crate::test_complete!("signal_kind_names");
    }

    #[test]
    fn signal_kind_display() {
        init_test("signal_kind_display");
        let interrupt = format!("{}", SignalKind::Interrupt);
        crate::assert_with_log!(interrupt == "SIGINT", "interrupt", "SIGINT", interrupt);
        let terminate = format!("{}", SignalKind::Terminate);
        crate::assert_with_log!(terminate == "SIGTERM", "terminate", "SIGTERM", terminate);
        crate::test_complete!("signal_kind_display");
    }

    #[cfg(unix)]
    #[test]
    fn signal_kind_raw_values() {
        init_test("signal_kind_raw_values");
        let interrupt = SignalKind::Interrupt.as_raw_value();
        crate::assert_with_log!(
            interrupt == libc::SIGINT,
            "interrupt",
            libc::SIGINT,
            interrupt
        );
        let terminate = SignalKind::Terminate.as_raw_value();
        crate::assert_with_log!(
            terminate == libc::SIGTERM,
            "terminate",
            libc::SIGTERM,
            terminate
        );
        let hangup = SignalKind::Hangup.as_raw_value();
        crate::assert_with_log!(hangup == libc::SIGHUP, "hangup", libc::SIGHUP, hangup);
        let user1 = SignalKind::User1.as_raw_value();
        crate::assert_with_log!(user1 == libc::SIGUSR1, "user1", libc::SIGUSR1, user1);
        let user2 = SignalKind::User2.as_raw_value();
        crate::assert_with_log!(user2 == libc::SIGUSR2, "user2", libc::SIGUSR2, user2);
        let child = SignalKind::Child.as_raw_value();
        crate::assert_with_log!(child == libc::SIGCHLD, "child", libc::SIGCHLD, child);
        let winch = SignalKind::WindowChange.as_raw_value();
        crate::assert_with_log!(winch == libc::SIGWINCH, "winch", libc::SIGWINCH, winch);
        let pipe = SignalKind::Pipe.as_raw_value();
        crate::assert_with_log!(pipe == libc::SIGPIPE, "pipe", libc::SIGPIPE, pipe);
        let alarm = SignalKind::Alarm.as_raw_value();
        crate::assert_with_log!(alarm == libc::SIGALRM, "alarm", libc::SIGALRM, alarm);
        crate::test_complete!("signal_kind_raw_values");
    }

    #[cfg(windows)]
    #[test]
    fn signal_kind_raw_values_windows_subset() {
        init_test("signal_kind_raw_values_windows_subset");
        let interrupt = SignalKind::Interrupt.as_raw_value();
        crate::assert_with_log!(
            interrupt == Some(libc::SIGINT),
            "interrupt",
            Some(libc::SIGINT),
            interrupt
        );
        let terminate = SignalKind::Terminate.as_raw_value();
        crate::assert_with_log!(
            terminate == Some(libc::SIGTERM),
            "terminate",
            Some(libc::SIGTERM),
            terminate
        );
        let quit = SignalKind::Quit.as_raw_value();
        crate::assert_with_log!(
            quit == Some(signal_hook::consts::SIGBREAK),
            "quit",
            Some(signal_hook::consts::SIGBREAK),
            quit
        );
        let user1 = SignalKind::User1.as_raw_value();
        crate::assert_with_log!(user1.is_none(), "user1 unsupported", true, user1.is_none());
        crate::test_complete!("signal_kind_raw_values_windows_subset");
    }

    // =========================================================================
    // Wave 51 – pure data-type trait coverage
    // =========================================================================

    #[test]
    fn signal_kind_debug_clone_copy_hash() {
        use std::collections::HashSet;
        let s = SignalKind::Interrupt;
        let dbg = format!("{s:?}");
        assert!(dbg.contains("Interrupt"), "{dbg}");
        let copied = s;
        let cloned = s;
        assert_eq!(copied, cloned);
        let mut set = HashSet::new();
        set.insert(s);
        assert!(set.contains(&SignalKind::Interrupt));
        assert!(!set.contains(&SignalKind::Terminate));
    }
}