1use std::str::FromStr;
4use std::{collections::HashMap, fmt::Display};
5
6use itertools::Itertools as _;
7
8use crate::{error, sys};
9
10#[derive(Clone, Copy, Eq, Hash, PartialEq)]
12pub enum TrapSignal {
13 Signal(sys::signal::Signal),
15 Debug,
17 Err,
19 Exit,
21 Return,
23}
24
25impl Display for TrapSignal {
26 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
27 f.write_str(self.as_str())
28 }
29}
30
31impl TrapSignal {
32 pub fn iterator() -> impl Iterator<Item = Self> {
34 const SIGNALS: &[TrapSignal] = &[TrapSignal::Debug, TrapSignal::Err, TrapSignal::Exit];
35
36 let iter = itertools::chain!(
37 SIGNALS.iter().copied(),
38 sys::signal::Signal::iterator().map(TrapSignal::Signal)
39 );
40
41 iter
42 }
43
44 pub const fn as_str(self) -> &'static str {
46 match self {
47 Self::Signal(s) => s.as_str(),
48 Self::Debug => "DEBUG",
49 Self::Err => "ERR",
50 Self::Exit => "EXIT",
51 Self::Return => "RETURN",
52 }
53 }
54}
55
56pub fn format_signals(
63 mut f: impl std::io::Write,
64 it: impl Iterator<Item = TrapSignal>,
65) -> Result<(), error::Error> {
66 let it = it
67 .filter_map(|s| i32::try_from(s).ok().map(|n| (s, n)))
68 .sorted_by(|a, b| Ord::cmp(&a.1, &b.1))
69 .format_with("\n", |s, f| f(&format_args!("{}) {}", s.1, s.0)));
70 write!(f, "{it}")?;
71 Ok(())
72}
73
74impl FromStr for TrapSignal {
76 type Err = error::Error;
77 fn from_str(s: &str) -> Result<Self, <Self as FromStr>::Err> {
78 if let Ok(n) = s.parse::<i32>() {
79 Self::try_from(n)
80 } else {
81 Self::try_from(s)
82 }
83 }
84}
85
86impl TryFrom<i32> for TrapSignal {
88 type Error = error::Error;
89 fn try_from(value: i32) -> Result<Self, Self::Error> {
90 Ok(match value {
94 0 => Self::Exit,
95 value => Self::Signal(
96 sys::signal::Signal::try_from(value)
97 .map_err(|_| error::ErrorKind::InvalidSignal(value.to_string()))?,
98 ),
99 })
100 }
101}
102
103impl TryFrom<&str> for TrapSignal {
105 type Error = error::Error;
106 fn try_from(value: &str) -> Result<Self, Self::Error> {
107 #[allow(unused_mut, reason = "only mutated on some platforms")]
108 let mut s = value.to_ascii_uppercase();
109
110 Ok(match s.as_str() {
111 "DEBUG" => Self::Debug,
112 "ERR" => Self::Err,
113 "EXIT" => Self::Exit,
114 "RETURN" => Self::Return,
115 _ => {
116 if !s.starts_with("SIG") {
119 s.insert_str(0, "SIG");
120 }
121 sys::signal::Signal::from_str(s.as_str())
122 .map(TrapSignal::Signal)
123 .map_err(|_| error::ErrorKind::InvalidSignal(value.into()))?
124 }
125 })
126 }
127}
128
129#[derive(Debug, Clone, Copy)]
131pub struct TrapSignalNumberError;
132
133impl TryFrom<TrapSignal> for i32 {
134 type Error = TrapSignalNumberError;
135 fn try_from(value: TrapSignal) -> Result<Self, Self::Error> {
136 Ok(match value {
137 TrapSignal::Signal(s) => s as Self,
138 TrapSignal::Exit => 0,
139 _ => return Err(TrapSignalNumberError),
140 })
141 }
142}
143
144#[derive(Clone, Default)]
146pub struct TrapHandlerConfig {
147 pub(crate) handlers: HashMap<TrapSignal, String>,
149 pub(crate) handler_depth: i32,
151}
152
153impl TrapHandlerConfig {
154 pub fn iter_handlers(&self) -> impl Iterator<Item = (TrapSignal, &str)> {
156 self.handlers
157 .iter()
158 .map(|(signal, cmd)| (*signal, cmd.as_str()))
159 }
160
161 pub fn get_handler(&self, signal_type: TrapSignal) -> Option<&str> {
167 self.handlers.get(&signal_type).map(|s| s.as_str())
168 }
169
170 pub fn register_handler(&mut self, signal_type: TrapSignal, command: String) {
177 let _ = self.handlers.insert(signal_type, command);
178 }
179
180 pub fn remove_handlers(&mut self, signal_type: TrapSignal) {
186 self.handlers.remove(&signal_type);
187 }
188}