1 | //! Utility functions that are not specific to X11. |
2 | //! |
3 | //! # RawFdContainer |
4 | //! |
5 | //! [`RawFdContainer`] is a variant of [`std::os::unix::io::RawFd`] with ownership semantics. This |
6 | //! means that the `RawFd` will be closed when the `RawFdContainer` is dropped. |
7 | //! |
8 | //! On non-`cfg(unix)`-systems, this is an empty type without methods. It still exists as a type so |
9 | //! that it can appear in interfaces, but it is not actually possible to construct an instance of |
10 | //! `RawFdContainer`. |
11 | |
12 | #[cfg (all(feature = "std" , unix))] |
13 | mod raw_fd_container { |
14 | use std::mem::forget; |
15 | use std::os::unix::io::{AsRawFd, IntoRawFd, RawFd}; |
16 | |
17 | /// A simple wrapper around RawFd that closes the fd on drop. |
18 | /// |
19 | /// On non-unix systems, this type is empty and does not provide |
20 | /// any method. |
21 | #[derive (Debug, Hash, PartialEq, Eq)] |
22 | pub struct RawFdContainer(RawFd); |
23 | |
24 | impl Drop for RawFdContainer { |
25 | fn drop(&mut self) { |
26 | let _ = nix::unistd::close(self.0); |
27 | } |
28 | } |
29 | |
30 | impl RawFdContainer { |
31 | /// Create a new `RawFdContainer` for the given `RawFd`. |
32 | /// |
33 | /// The `RawFdContainer` takes ownership of the `RawFd` and closes it on drop. |
34 | pub fn new(fd: RawFd) -> Self { |
35 | RawFdContainer(fd) |
36 | } |
37 | |
38 | /// Tries to clone the `RawFdContainer` creating a new FD |
39 | /// with `dup`. The new `RawFdContainer` will take ownership |
40 | /// of the `dup`ed version, whereas the original `RawFdContainer` |
41 | /// will keep the ownership of its FD. |
42 | pub fn try_clone(&self) -> Result<Self, std::io::Error> { |
43 | Ok(Self::new(nix::unistd::dup(self.0)?)) |
44 | } |
45 | |
46 | /// Get the `RawFd` out of this `RawFdContainer`. |
47 | /// |
48 | /// This function would be an implementation of `IntoRawFd` if that were possible. However, it |
49 | /// causes a conflict with an `impl` from libcore... |
50 | pub fn into_raw_fd(self) -> RawFd { |
51 | let fd = self.0; |
52 | forget(self); |
53 | fd |
54 | } |
55 | |
56 | /// Consumes the `RawFdContainer` and closes the wrapped FD with |
57 | /// the `close` system call. |
58 | /// |
59 | /// This is similar to dropping the `RawFdContainer`, but it allows |
60 | /// the caller to handle errors. |
61 | pub fn close(self) -> Result<(), std::io::Error> { |
62 | let fd = self.into_raw_fd(); |
63 | nix::unistd::close(fd).map_err(|e| e.into()) |
64 | } |
65 | } |
66 | |
67 | impl<T: IntoRawFd> From<T> for RawFdContainer { |
68 | fn from(fd: T) -> Self { |
69 | Self::new(fd.into_raw_fd()) |
70 | } |
71 | } |
72 | |
73 | impl AsRawFd for RawFdContainer { |
74 | fn as_raw_fd(&self) -> RawFd { |
75 | self.0 |
76 | } |
77 | } |
78 | } |
79 | |
80 | #[cfg (not(all(feature = "std" , unix)))] |
81 | mod raw_fd_container { |
82 | use core::convert::Infallible; |
83 | |
84 | /// A simple wrapper around RawFd that closes the fd on drop. |
85 | /// |
86 | /// On non-unix systems, this type is empty and does not provide |
87 | /// any method. |
88 | #[derive (Debug, Hash, PartialEq, Eq)] |
89 | pub struct RawFdContainer(Infallible); |
90 | |
91 | impl Drop for RawFdContainer { |
92 | fn drop(&mut self) { |
93 | // This function exists for symmetry with cfg(unix) |
94 | match self.0 {} |
95 | } |
96 | } |
97 | } |
98 | |
99 | pub use raw_fd_container::RawFdContainer; |
100 | |
101 | mod pretty_printer { |
102 | use core::fmt::{Debug, Formatter, Result}; |
103 | |
104 | /// A helper to pretty-print an enumeration value. |
105 | /// |
106 | /// This function prints the given number. If it matches one of the provided variants, that |
107 | /// match is used. Otherwise, the number is printed as a decimal. |
108 | /// |
109 | /// In alternate mode, the second string in the given array is used, else the first. |
110 | pub(crate) fn pretty_print_enum( |
111 | fmt: &mut Formatter<'_>, |
112 | value: u32, |
113 | cases: &[(u32, &str, &str)], |
114 | ) -> Result { |
115 | for (variant, name1, name2) in cases { |
116 | if &value == variant { |
117 | if fmt.alternate() { |
118 | return fmt.write_str(name2); |
119 | } else { |
120 | return fmt.write_str(name1); |
121 | } |
122 | } |
123 | } |
124 | Debug::fmt(&value, fmt) |
125 | } |
126 | |
127 | /// A helper to pretty-print a bitmask. |
128 | /// |
129 | /// This function prints the given number. All bit-matches with the given variants are printed. |
130 | /// Any left-over number is printed as a decimal. |
131 | /// |
132 | /// In alternate mode, the second string in the given array is used, else the first. |
133 | pub(crate) fn pretty_print_bitmask( |
134 | fmt: &mut Formatter<'_>, |
135 | value: u32, |
136 | cases: &[(u32, &str, &str)], |
137 | ) -> Result { |
138 | // First, figure out if there are any bits not covered by any case |
139 | let known_bits = cases.iter().fold(0, |acc, (value, _, _)| acc | value); |
140 | let remaining = value & !known_bits; |
141 | let mut already_printed = if value == 0 || remaining != 0 { |
142 | Debug::fmt(&remaining, fmt)?; |
143 | true |
144 | } else { |
145 | false |
146 | }; |
147 | for (variant, name1, name2) in cases { |
148 | if variant & value != 0 { |
149 | if already_printed { |
150 | fmt.write_str(" | " )?; |
151 | } |
152 | already_printed = true; |
153 | if fmt.alternate() { |
154 | fmt.write_str(name2)?; |
155 | } else { |
156 | fmt.write_str(name1)?; |
157 | } |
158 | } |
159 | } |
160 | Ok(()) |
161 | } |
162 | |
163 | #[cfg (test)] |
164 | mod test { |
165 | use super::{pretty_print_bitmask, pretty_print_enum}; |
166 | use alloc::format; |
167 | use core::fmt::{Display, Formatter, Result}; |
168 | |
169 | type CallbackType = fn(&mut Formatter<'_>, u32, &[(u32, &str, &str)]) -> Result; |
170 | |
171 | struct CallbackFormating<'a, 'b> { |
172 | callback: CallbackType, |
173 | value: u32, |
174 | cases: &'a [(u32, &'b str, &'b str)], |
175 | } |
176 | |
177 | fn new_enum<'a, 'b>( |
178 | value: u32, |
179 | cases: &'a [(u32, &'b str, &'b str)], |
180 | ) -> CallbackFormating<'a, 'b> { |
181 | CallbackFormating { |
182 | callback: pretty_print_enum, |
183 | value, |
184 | cases, |
185 | } |
186 | } |
187 | |
188 | fn new_bitmask<'a, 'b>( |
189 | value: u32, |
190 | cases: &'a [(u32, &'b str, &'b str)], |
191 | ) -> CallbackFormating<'a, 'b> { |
192 | CallbackFormating { |
193 | callback: pretty_print_bitmask, |
194 | value, |
195 | cases, |
196 | } |
197 | } |
198 | |
199 | impl Display for CallbackFormating<'_, '_> { |
200 | fn fmt(&self, f: &mut Formatter<'_>) -> Result { |
201 | (self.callback)(f, self.value, self.cases) |
202 | } |
203 | } |
204 | |
205 | #[test ] |
206 | fn test_enum() { |
207 | let cases = [(0, "zero" , "ZERO" ), (42, "the answer" , "ANSWER" )]; |
208 | let printer = new_enum(0, &cases); |
209 | assert_eq!(&format!("{}" , printer), "zero" ); |
210 | assert_eq!(&format!("{:#}" , printer), "ZERO" ); |
211 | let printer = new_enum(1, &cases); |
212 | assert_eq!(&format!("{}" , printer), "1" ); |
213 | assert_eq!(&format!("{:#}" , printer), "1" ); |
214 | let printer = new_enum(42, &cases); |
215 | assert_eq!(&format!("{}" , printer), "the answer" ); |
216 | assert_eq!(&format!("{:#}" , printer), "ANSWER" ); |
217 | } |
218 | |
219 | #[test ] |
220 | fn test_bitmask() { |
221 | let bits = [ |
222 | (1 << 5, "b5" , "B5" ), |
223 | (1 << 1, "b1" , "B1" ), |
224 | (1 << 0, "unused" , "UNUSED" ), |
225 | ]; |
226 | let printer = new_bitmask(8, &bits); |
227 | assert_eq!(&format!("{}" , printer), "8" ); |
228 | assert_eq!(&format!("{:#}" , printer), "8" ); |
229 | let printer = new_bitmask(32, &bits); |
230 | assert_eq!(&format!("{}" , printer), "b5" ); |
231 | assert_eq!(&format!("{:#}" , printer), "B5" ); |
232 | let printer = new_bitmask(34, &bits); |
233 | assert_eq!(&format!("{}" , printer), "b5 | b1" ); |
234 | assert_eq!(&format!("{:#}" , printer), "B5 | B1" ); |
235 | let printer = new_bitmask(42, &bits); |
236 | assert_eq!(&format!("{}" , printer), "8 | b5 | b1" ); |
237 | assert_eq!(&format!("{:#}" , printer), "8 | B5 | B1" ); |
238 | } |
239 | } |
240 | } |
241 | |
242 | pub(crate) use pretty_printer::{pretty_print_bitmask, pretty_print_enum}; |
243 | |