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))]
13mod 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)))]
81mod 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
99pub use raw_fd_container::RawFdContainer;
100
101mod 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
242pub(crate) use pretty_printer::{pretty_print_bitmask, pretty_print_enum};
243