x11rb_protocol/
utils.rs

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::os::unix::io::OwnedFd;
15
16    pub(crate) type RawFdContainer = OwnedFd;
17}
18
19#[cfg(not(all(feature = "std", unix)))]
20mod 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
54pub type RawFdContainer = raw_fd_container::RawFdContainer;
55
56mod 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
197pub(crate) use pretty_printer::{pretty_print_bitmask, pretty_print_enum};