yash_env/trap/cond.rs
1// This file is part of yash, an extended POSIX shell.
2// Copyright (C) 2023 WATANABE Yuki
3//
4// This program is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8//
9// This program is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12// GNU General Public License for more details.
13//
14// You should have received a copy of the GNU General Public License
15// along with this program. If not, see <https://www.gnu.org/licenses/>.
16
17//! Items that define trap conditions
18
19#[cfg(doc)]
20use super::state::Action;
21use crate::signal;
22use crate::system::Signals;
23use itertools::Itertools as _;
24use std::borrow::Cow;
25
26/// Condition under which an [`Action`] is executed
27#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
28#[non_exhaustive]
29pub enum Condition {
30 /// When the shell exits
31 Exit,
32 /// When the specified signal is delivered to the shell process
33 Signal(signal::Number),
34}
35
36impl From<signal::Number> for Condition {
37 fn from(number: signal::Number) -> Self {
38 Self::Signal(number)
39 }
40}
41
42/// Conversion from raw signal number to `Condition`
43///
44/// If the number is zero, the result is [`Condition::Exit`]. Otherwise, the
45/// result is [`Condition::Signal`] with the signal number.
46impl From<signal::RawNumber> for Condition {
47 fn from(number: signal::RawNumber) -> Self {
48 if let Ok(non_zero) = number.try_into() {
49 Self::Signal(signal::Number::from_raw_unchecked(non_zero))
50 } else {
51 Self::Exit
52 }
53 }
54}
55
56impl From<Condition> for signal::RawNumber {
57 fn from(cond: Condition) -> Self {
58 match cond {
59 Condition::Exit => 0,
60 Condition::Signal(number) => number.as_raw(),
61 }
62 }
63}
64
65impl Condition {
66 /// Converts this `Condition` to a `String`.
67 ///
68 /// The result is an uppercase string representing the condition such as
69 /// `"EXIT"` and `"TERM"`. Signal names are obtained from
70 /// [`Signals::sig2str`].
71 #[must_use]
72 pub fn to_string<S: Signals>(&self, system: &S) -> Cow<'static, str> {
73 match self {
74 Self::Exit => Cow::Borrowed("EXIT"),
75 Self::Signal(number) => system.sig2str(*number).unwrap_or(Cow::Borrowed("?")),
76 }
77 }
78
79 /// Returns an iterator over all possible conditions.
80 ///
81 /// The iterator yields all the conditions supported by the given `Signals`
82 /// implementation.
83 /// The iteration starts with [`Condition::Exit`], followed by all the
84 /// signals in the ascending order of their signal numbers.
85 // TODO Most part of this function is duplicated from yash_builtin::kill::print::all_signals.
86 // Consider refactoring to share the code. Note that all_signals does not
87 // deduplicate the signals.
88 pub fn iter<S: Signals>(system: &S) -> impl Iterator<Item = Condition> + '_ {
89 let non_real_time = S::NAMED_SIGNALS
90 .iter()
91 .filter_map(|&(_, number)| Some(Condition::Signal(number?)));
92 let non_real_time_count = S::NAMED_SIGNALS.len();
93
94 let real_time = system.iter_sigrt().map(Condition::Signal);
95 let real_time_count = real_time.size_hint().1.unwrap_or_default();
96
97 let mut conditions = Vec::with_capacity(1 + non_real_time_count + real_time_count);
98 conditions.push(Condition::Exit);
99 conditions.extend(non_real_time);
100 conditions.extend(real_time);
101 conditions.sort();
102 // Some names may share the same number, so deduplicate.
103 conditions.into_iter().dedup()
104 }
105}
106
107#[cfg(test)]
108mod tests {
109 use super::*;
110 use crate::system::r#virtual::VirtualSystem;
111
112 #[test]
113 fn condition_iter_is_sorted() {
114 let system = VirtualSystem::new();
115 let iter = Condition::iter(&system);
116 assert!(iter.is_sorted());
117 }
118
119 #[test]
120 fn condition_iter_is_unique() {
121 let system = VirtualSystem::new();
122 let iter = Condition::iter(&system);
123 let iter_dedup = Condition::iter(&system).dedup();
124 assert!(iter.eq(iter_dedup));
125 }
126}