use std::io;
use super::{SignalKind, signal};
#[derive(Debug, Clone)]
pub struct CtrlCError {
message: &'static str,
}
impl CtrlCError {
const fn unavailable() -> Self {
Self {
message: "Ctrl+C handling is unavailable on this platform/build",
}
}
}
impl std::fmt::Display for CtrlCError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.message)
}
}
impl std::error::Error for CtrlCError {}
impl From<CtrlCError> for io::Error {
fn from(e: CtrlCError) -> Self {
Self::new(io::ErrorKind::Unsupported, e)
}
}
pub async fn ctrl_c() -> io::Result<()> {
let mut stream = signal(SignalKind::interrupt())
.map_err(|_| io::Error::new(io::ErrorKind::Unsupported, CtrlCError::unavailable()))?;
match stream.recv().await {
Some(()) => Ok(()),
None => Err(io::Error::new(
io::ErrorKind::UnexpectedEof,
"ctrl_c signal stream closed unexpectedly",
)),
}
}
#[must_use]
pub fn is_available() -> bool {
#[cfg(any(unix, windows))]
{
signal(SignalKind::interrupt()).is_ok()
}
#[cfg(not(any(unix, windows)))]
{
false
}
}
#[cfg(test)]
mod tests {
use super::*;
fn init_test(name: &str) {
crate::test_utils::init_test_logging();
crate::test_phase!(name);
}
#[test]
fn ctrl_c_not_available() {
init_test("ctrl_c_not_available");
let available = is_available();
#[cfg(any(unix, windows))]
crate::assert_with_log!(available, "available", true, available);
#[cfg(not(any(unix, windows)))]
crate::assert_with_log!(!available, "not available", false, available);
crate::test_complete!("ctrl_c_not_available");
}
#[test]
fn ctrl_c_error_display() {
init_test("ctrl_c_error_display");
let err = CtrlCError::unavailable();
let msg = format!("{err}");
let contains = msg.contains("unavailable");
crate::assert_with_log!(contains, "contains unavailable", true, contains);
crate::test_complete!("ctrl_c_error_display");
}
}