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(
90 mut f: impl std::io::Write,
91 it: impl Iterator<Item = TrapSignal>,
92) -> Result<(), error::Error> {
93 let it = it
94 .filter_map(|s| i32::try_from(s).ok().map(|n| (s, n)))
95 .sorted_by(|a, b| Ord::cmp(&a.1, &b.1))
96 .format_with("\n", |s, f| f(&format_args!("{}) {}", s.1, s.0)));
97 write!(f, "{it}")?;
98 Ok(())
99}
100
101impl FromStr for TrapSignal {
103 type Err = error::Error;
104 fn from_str(s: &str) -> Result<Self, <Self as FromStr>::Err> {
105 if let Ok(n) = s.parse::<i32>() {
106 Self::try_from(n)
107 } else {
108 Self::try_from(s)
109 }
110 }
111}
112
113impl TryFrom<i32> for TrapSignal {
115 type Error = error::Error;
116 fn try_from(value: i32) -> Result<Self, Self::Error> {
117 Ok(match value {
122 0 => Self::Exit,
123 value => Self::Signal(
124 sys::signal::Signal::try_from(value)
125 .map_err(|_| error::ErrorKind::InvalidSignal(value.to_string()))?,
126 ),
127 })
128 }
129}
130
131impl TryFrom<&str> for TrapSignal {
133 type Error = error::Error;
134 fn try_from(value: &str) -> Result<Self, Self::Error> {
135 #[allow(unused_mut, reason = "only mutated on some platforms")]
136 let mut s = value.to_ascii_uppercase();
137
138 Ok(match s.as_str() {
139 "DEBUG" => Self::Debug,
140 "ERR" => Self::Err,
141 "EXIT" => Self::Exit,
142 "RETURN" => Self::Return,
143 _ => {
144 if !s.starts_with("SIG") {
148 s.insert_str(0, "SIG");
149 }
150 sys::signal::Signal::from_str(s.as_str())
151 .map(TrapSignal::Signal)
152 .map_err(|_| error::ErrorKind::InvalidSignal(value.into()))?
153 }
154 })
155 }
156}
157
158#[derive(Debug, Clone, Copy)]
160pub struct TrapSignalNumberError;
161
162impl TryFrom<TrapSignal> for i32 {
163 type Error = TrapSignalNumberError;
164 fn try_from(value: TrapSignal) -> Result<Self, Self::Error> {
165 Ok(match value {
166 TrapSignal::Signal(s) => s as Self,
167 TrapSignal::Exit => 0,
168 _ => return Err(TrapSignalNumberError),
169 })
170 }
171}
172
173#[derive(Clone, Default)]
175#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
176pub struct TrapHandler {
177 pub command: String,
179 pub source_info: crate::SourceInfo,
181}
182
183#[derive(Clone, Default)]
185#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
186pub struct TrapHandlerConfig {
187 handlers: HashMap<TrapSignal, TrapHandler>,
189}
190
191impl TrapHandlerConfig {
192 pub fn iter_handlers(&self) -> impl Iterator<Item = (TrapSignal, &TrapHandler)> {
194 self.handlers
195 .iter()
196 .map(|(signal, handler)| (*signal, handler))
197 }
198
199 pub fn get_handler(&self, signal_type: TrapSignal) -> Option<&TrapHandler> {
205 self.handlers.get(&signal_type)
206 }
207
208 pub fn handles(&self, signal_type: TrapSignal) -> bool {
210 self.handlers.contains_key(&signal_type)
211 }
212
213 pub fn register_handler(
222 &mut self,
223 signal_type: TrapSignal,
224 command: String,
225 source_info: crate::SourceInfo,
226 ) {
227 let _ = self.handlers.insert(
228 signal_type,
229 TrapHandler {
230 command,
231 source_info,
232 },
233 );
234 }
235
236 pub fn remove_handlers(&mut self, signal_type: TrapSignal) {
242 self.handlers.remove(&signal_type);
243 }
244}