rusty-pv 0.1.0

Pipe viewer — a Rust port of Andrew Wood's `pv(1)` with progress bar, ETA, rate display, token-bucket rate limiting, IEC/SI unit math, SIGWINCH-aware terminal redraw, SIGUSR1 size refresh, multi-instance cursor coordination, and a typed library API.
Documentation
//! Unix signal handlers for SIGUSR1 + SIGWINCH (Unix-only, CLI-feature-gated).
//!
//! FR-030, AD-010, HINT-013. SIGUSR1 sets an atomic flag the main loop polls
//! to re-stat the input file and refresh the `-s` size hint. SIGWINCH sets an
//! atomic flag the display tick polls to re-query terminal width.
//!
//! On Windows, this module is intentionally absent — callers that import it
//! must `#[cfg(unix)]`-gate the import.

#![cfg(unix)]

use std::sync::Arc;
use std::sync::atomic::{AtomicBool, Ordering};

/// Shared flags set by signal handlers and polled by the main loop.
#[derive(Debug, Clone, Default)]
pub struct SignalFlags {
    /// Set when SIGUSR1 has been received (FR-030 — re-stat input file).
    pub sigusr1: Arc<AtomicBool>,
    /// Set when SIGWINCH has been received (FR-019 — re-query terminal width).
    pub sigwinch: Arc<AtomicBool>,
}

impl SignalFlags {
    /// Construct a fresh, cleared set of flags.
    #[must_use]
    pub fn new() -> Self {
        Self::default()
    }

    /// Atomically consume the SIGUSR1 flag. Returns `true` if it was set.
    pub fn take_sigusr1(&self) -> bool {
        self.sigusr1.swap(false, Ordering::AcqRel)
    }

    /// Atomically consume the SIGWINCH flag. Returns `true` if it was set.
    pub fn take_sigwinch(&self) -> bool {
        self.sigwinch.swap(false, Ordering::AcqRel)
    }
}

/// Register signal-hook handlers for SIGUSR1 + SIGWINCH backed by `flags`.
///
/// Returns the registered guards; drop them to unregister. The handlers
/// themselves are signal-safe: they only `store(true)` on the relevant atomic
/// and return immediately.
///
/// # Errors
///
/// Returns the underlying `signal-hook` registration error if the syscall
/// fails (typically not possible for SIGUSR1/SIGWINCH on standard Unix).
pub fn register(flags: &SignalFlags) -> Result<Registration, std::io::Error> {
    use signal_hook::consts::{SIGUSR1, SIGWINCH};
    use signal_hook::flag;

    let sigusr1_id = flag::register(SIGUSR1, flags.sigusr1.clone())?;
    let sigwinch_id = flag::register(SIGWINCH, flags.sigwinch.clone())?;
    Ok(Registration {
        _sigusr1: sigusr1_id,
        _sigwinch: sigwinch_id,
    })
}

/// RAII registration guard. Drop to remove the handlers.
#[derive(Debug)]
pub struct Registration {
    _sigusr1: signal_hook::SigId,
    _sigwinch: signal_hook::SigId,
}

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

    #[test]
    fn flags_round_trip() {
        let f = SignalFlags::new();
        assert!(!f.take_sigusr1());
        assert!(!f.take_sigwinch());
        f.sigusr1.store(true, Ordering::Release);
        f.sigwinch.store(true, Ordering::Release);
        assert!(f.take_sigusr1());
        assert!(f.take_sigwinch());
        // After taking, flags are cleared.
        assert!(!f.take_sigusr1());
        assert!(!f.take_sigwinch());
    }
}