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::os::unix::io::OwnedFd; |
15 | |
16 | pub(crate) type RawFdContainer = OwnedFd; |
17 | } |
18 | |
19 | #[cfg (not(all(feature = "std" , unix)))] |
20 | mod raw_fd_container { |
21 | use core::convert::Infallible; |
22 | |
23 | #[derive (Debug)] |
24 | #[doc (hidden)] |
25 | pub struct RawFdContainer(Infallible); |
26 | |
27 | impl Drop for RawFdContainer { |
28 | fn drop(&mut self) { |
29 | // This function exists for symmetry with cfg(unix) |
30 | match self.0 {} |
31 | } |
32 | } |
33 | } |
34 | |
35 | /// A type representative of the file descriptors as they are sent to and from the X server. |
36 | /// |
37 | /// On `cfg(unix)` platforms, this is a type alias for [`std::os::unix::io::OwnedFd`]. See the |
38 | /// documentation for that type for more information on how it should be used. In most cases it |
39 | /// can be cast into a [`File`] or [`UnixStream`], or otherwise downgraded into the actual |
40 | /// underlying file descriptor. |
41 | /// |
42 | /// On non-Unix platforms, this is an uninhabited type in the same vogue as [`Void`]. As handle |
43 | /// passing is an undefined operation on non-Unix implementations of the X11 protocol, instances |
44 | /// of this type cannot exist. No operations can be called on this type. If handle passing is ever |
45 | /// added to any reference implementation of the X11 protocol, this type will be changed to |
46 | /// something that can be used to represent the file descriptors. |
47 | /// |
48 | /// Consumers of this type should be careful to check for `cfg(unix)` before using it in any |
49 | /// meaningful way. Otherwise, the program will not compile on non-Unix platforms. |
50 | /// |
51 | /// [`File`]: std::fs::File |
52 | /// [`UnixStream`]: std::os::unix::net::UnixStream |
53 | /// [`Void`]: https://docs.rs/void/latest/void/enum.Void.html |
54 | pub type RawFdContainer = raw_fd_container::RawFdContainer; |
55 | |
56 | mod pretty_printer { |
57 | use core::fmt::{Debug, Formatter, Result}; |
58 | |
59 | /// A helper to pretty-print an enumeration value. |
60 | /// |
61 | /// This function prints the given number. If it matches one of the provided variants, that |
62 | /// match is used. Otherwise, the number is printed as a decimal. |
63 | /// |
64 | /// In alternate mode, the second string in the given array is used, else the first. |
65 | pub(crate) fn pretty_print_enum( |
66 | fmt: &mut Formatter<'_>, |
67 | value: u32, |
68 | cases: &[(u32, &str, &str)], |
69 | ) -> Result { |
70 | for (variant, name1, name2) in cases { |
71 | if &value == variant { |
72 | if fmt.alternate() { |
73 | return fmt.write_str(name2); |
74 | } else { |
75 | return fmt.write_str(name1); |
76 | } |
77 | } |
78 | } |
79 | Debug::fmt(&value, fmt) |
80 | } |
81 | |
82 | /// A helper to pretty-print a bitmask. |
83 | /// |
84 | /// This function prints the given number. All bit-matches with the given variants are printed. |
85 | /// Any left-over number is printed as a decimal. |
86 | /// |
87 | /// In alternate mode, the second string in the given array is used, else the first. |
88 | pub(crate) fn pretty_print_bitmask( |
89 | fmt: &mut Formatter<'_>, |
90 | value: u32, |
91 | cases: &[(u32, &str, &str)], |
92 | ) -> Result { |
93 | // First, figure out if there are any bits not covered by any case |
94 | let known_bits = cases.iter().fold(0, |acc, (value, _, _)| acc | value); |
95 | let remaining = value & !known_bits; |
96 | let mut already_printed = if value == 0 || remaining != 0 { |
97 | Debug::fmt(&remaining, fmt)?; |
98 | true |
99 | } else { |
100 | false |
101 | }; |
102 | for (variant, name1, name2) in cases { |
103 | if variant & value != 0 { |
104 | if already_printed { |
105 | fmt.write_str(" | " )?; |
106 | } |
107 | already_printed = true; |
108 | if fmt.alternate() { |
109 | fmt.write_str(name2)?; |
110 | } else { |
111 | fmt.write_str(name1)?; |
112 | } |
113 | } |
114 | } |
115 | Ok(()) |
116 | } |
117 | |
118 | #[cfg (test)] |
119 | mod test { |
120 | use super::{pretty_print_bitmask, pretty_print_enum}; |
121 | use alloc::format; |
122 | use core::fmt::{Display, Formatter, Result}; |
123 | |
124 | type CallbackType = fn(&mut Formatter<'_>, u32, &[(u32, &str, &str)]) -> Result; |
125 | |
126 | struct CallbackFormating<'a, 'b> { |
127 | callback: CallbackType, |
128 | value: u32, |
129 | cases: &'a [(u32, &'b str, &'b str)], |
130 | } |
131 | |
132 | fn new_enum<'a, 'b>( |
133 | value: u32, |
134 | cases: &'a [(u32, &'b str, &'b str)], |
135 | ) -> CallbackFormating<'a, 'b> { |
136 | CallbackFormating { |
137 | callback: pretty_print_enum, |
138 | value, |
139 | cases, |
140 | } |
141 | } |
142 | |
143 | fn new_bitmask<'a, 'b>( |
144 | value: u32, |
145 | cases: &'a [(u32, &'b str, &'b str)], |
146 | ) -> CallbackFormating<'a, 'b> { |
147 | CallbackFormating { |
148 | callback: pretty_print_bitmask, |
149 | value, |
150 | cases, |
151 | } |
152 | } |
153 | |
154 | impl Display for CallbackFormating<'_, '_> { |
155 | fn fmt(&self, f: &mut Formatter<'_>) -> Result { |
156 | (self.callback)(f, self.value, self.cases) |
157 | } |
158 | } |
159 | |
160 | #[test ] |
161 | fn test_enum() { |
162 | let cases = [(0, "zero" , "ZERO" ), (42, "the answer" , "ANSWER" )]; |
163 | let printer = new_enum(0, &cases); |
164 | assert_eq!(&format!("{}" , printer), "zero" ); |
165 | assert_eq!(&format!("{:#}" , printer), "ZERO" ); |
166 | let printer = new_enum(1, &cases); |
167 | assert_eq!(&format!("{}" , printer), "1" ); |
168 | assert_eq!(&format!("{:#}" , printer), "1" ); |
169 | let printer = new_enum(42, &cases); |
170 | assert_eq!(&format!("{}" , printer), "the answer" ); |
171 | assert_eq!(&format!("{:#}" , printer), "ANSWER" ); |
172 | } |
173 | |
174 | #[test ] |
175 | fn test_bitmask() { |
176 | let bits = [ |
177 | (1 << 5, "b5" , "B5" ), |
178 | (1 << 1, "b1" , "B1" ), |
179 | (1 << 0, "unused" , "UNUSED" ), |
180 | ]; |
181 | let printer = new_bitmask(8, &bits); |
182 | assert_eq!(&format!("{}" , printer), "8" ); |
183 | assert_eq!(&format!("{:#}" , printer), "8" ); |
184 | let printer = new_bitmask(32, &bits); |
185 | assert_eq!(&format!("{}" , printer), "b5" ); |
186 | assert_eq!(&format!("{:#}" , printer), "B5" ); |
187 | let printer = new_bitmask(34, &bits); |
188 | assert_eq!(&format!("{}" , printer), "b5 | b1" ); |
189 | assert_eq!(&format!("{:#}" , printer), "B5 | B1" ); |
190 | let printer = new_bitmask(42, &bits); |
191 | assert_eq!(&format!("{}" , printer), "8 | b5 | b1" ); |
192 | assert_eq!(&format!("{:#}" , printer), "8 | B5 | B1" ); |
193 | } |
194 | } |
195 | } |
196 | |
197 | pub(crate) use pretty_printer::{pretty_print_bitmask, pretty_print_enum}; |
198 | |