1use std::collections::HashMap;
4use std::fmt::Display;
5use std::str::FromStr;
6
7use itertools::Itertools as _;
8
9use crate::{error, sys};
10
11#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
13pub enum TrapSignal {
14 Signal(sys::signal::Signal),
16 Debug,
18 Err,
20 Exit,
22 Return,
24}
25
26#[cfg(feature = "serde")]
27impl serde::Serialize for TrapSignal {
28 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
29 where
30 S: serde::Serializer,
31 {
32 serializer.serialize_str(self.as_str())
33 }
34}
35
36#[cfg(feature = "serde")]
37impl<'de> serde::Deserialize<'de> for TrapSignal {
38 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
39 where
40 D: serde::Deserializer<'de>,
41 {
42 let s = String::deserialize(deserializer)?;
43 Self::try_from(s.as_str()).map_err(serde::de::Error::custom)
44 }
45}
46
47impl Display for TrapSignal {
48 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
49 f.write_str(self.as_str())
50 }
51}
52
53impl TrapSignal {
54 pub fn iterator() -> impl Iterator<Item = Self> {
56 const SIGNALS: &[TrapSignal] = &[TrapSignal::Debug, TrapSignal::Err, TrapSignal::Exit];
57
58 let iter = itertools::chain!(
59 SIGNALS.iter().copied(),
60 sys::signal::Signal::iterator().map(TrapSignal::Signal)
61 );
62
63 iter
64 }
65
66 pub const fn as_str(self) -> &'static str {
69 match self {
70 Self::Signal(s) => s.as_str(),
71 Self::Debug => "DEBUG",
72 Self::Err => "ERR",
73 Self::Exit => "EXIT",
74 Self::Return => "RETURN",
75 }
76 }
77}
78
79pub fn format_signals(
86 mut f: impl std::io::Write,
87 it: impl Iterator<Item = TrapSignal>,
88) -> Result<(), error::Error> {
89 let it = it
90 .filter_map(|s| i32::try_from(s).ok().map(|n| (s, n)))
91 .sorted_by(|a, b| Ord::cmp(&a.1, &b.1))
92 .format_with("\n", |s, f| f(&format_args!("{}) {}", s.1, s.0)));
93 write!(f, "{it}")?;
94 Ok(())
95}
96
97impl FromStr for TrapSignal {
99 type Err = error::Error;
100 fn from_str(s: &str) -> Result<Self, <Self as FromStr>::Err> {
101 if let Ok(n) = s.parse::<i32>() {
102 Self::try_from(n)
103 } else {
104 Self::try_from(s)
105 }
106 }
107}
108
109impl TryFrom<i32> for TrapSignal {
111 type Error = error::Error;
112 fn try_from(value: i32) -> Result<Self, Self::Error> {
113 Ok(match value {
118 0 => Self::Exit,
119 value => Self::Signal(
120 sys::signal::Signal::try_from(value)
121 .map_err(|_| error::ErrorKind::InvalidSignal(value.to_string()))?,
122 ),
123 })
124 }
125}
126
127impl TryFrom<&str> for TrapSignal {
129 type Error = error::Error;
130 fn try_from(value: &str) -> Result<Self, Self::Error> {
131 #[allow(unused_mut, reason = "only mutated on some platforms")]
132 let mut s = value.to_ascii_uppercase();
133
134 Ok(match s.as_str() {
135 "DEBUG" => Self::Debug,
136 "ERR" => Self::Err,
137 "EXIT" => Self::Exit,
138 "RETURN" => Self::Return,
139 _ => {
140 if !s.starts_with("SIG") {
144 s.insert_str(0, "SIG");
145 }
146 sys::signal::Signal::from_str(s.as_str())
147 .map(TrapSignal::Signal)
148 .map_err(|_| error::ErrorKind::InvalidSignal(value.into()))?
149 }
150 })
151 }
152}
153
154#[derive(Debug, Clone, Copy)]
156pub struct TrapSignalNumberError;
157
158impl TryFrom<TrapSignal> for i32 {
159 type Error = TrapSignalNumberError;
160 fn try_from(value: TrapSignal) -> Result<Self, Self::Error> {
161 Ok(match value {
162 TrapSignal::Signal(s) => s as Self,
163 TrapSignal::Exit => 0,
164 _ => return Err(TrapSignalNumberError),
165 })
166 }
167}
168
169#[derive(Clone, Default)]
171#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
172pub struct TrapHandler {
173 pub command: String,
175 pub source_info: crate::SourceInfo,
177}
178
179#[derive(Clone, Default)]
181#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
182pub struct TrapHandlerConfig {
183 handlers: HashMap<TrapSignal, TrapHandler>,
185}
186
187impl TrapHandlerConfig {
188 pub fn iter_handlers(&self) -> impl Iterator<Item = (TrapSignal, &TrapHandler)> {
190 self.handlers
191 .iter()
192 .map(|(signal, handler)| (*signal, handler))
193 }
194
195 pub fn get_handler(&self, signal_type: TrapSignal) -> Option<&TrapHandler> {
201 self.handlers.get(&signal_type)
202 }
203
204 pub fn handles(&self, signal_type: TrapSignal) -> bool {
206 self.handlers.contains_key(&signal_type)
207 }
208
209 pub fn register_handler(
218 &mut self,
219 signal_type: TrapSignal,
220 command: String,
221 source_info: crate::SourceInfo,
222 ) {
223 let _ = self.handlers.insert(
224 signal_type,
225 TrapHandler {
226 command,
227 source_info,
228 },
229 );
230 }
231
232 pub fn remove_handlers(&mut self, signal_type: TrapSignal) {
238 self.handlers.remove(&signal_type);
239 }
240}