breadx/
error.rs

1//               Copyright John Nunley, 2022.
2// Distributed under the Boost Software License, Version 1.0.
3//       (See accompanying file LICENSE or copy at
4//         https://www.boost.org/LICENSE_1_0.txt)
5
6use core::any::type_name;
7use core::fmt;
8use core::str;
9
10use alloc::string::String;
11use alloc::string::ToString;
12use x11rb_protocol::x11_utils::X11Error;
13use x11rb_protocol::{
14    errors::{ConnectError, ParseError},
15    protocol::xproto::{SetupAuthenticate, SetupFailed},
16};
17
18cfg_std! {
19    use std::error::Error as StdError;
20    use std::io::Error as IoError;
21    use std::io::ErrorKind;
22}
23
24cfg_std_unix! {
25    use nix::errno::Errno;
26}
27
28/// An error that may occur during operation of this crate.
29pub struct Error {
30    /// The inner details of the error.
31    inner: Inner,
32    /// Whether or not this error occurred during connection
33    /// initialization.
34    initialization: bool,
35}
36
37/// The innards of the [`Error`] struct.
38///
39/// Making this type not public allows us to change it without it being
40/// a breaking change.
41enum Inner {
42    /// We tried to run an operation that is not supported.
43    #[allow(dead_code)]
44    Unsupported(Unsupported),
45    /// A custom message.
46    Message(String),
47    /// We did something that has put the X11 connection into an invalid state.
48    ///
49    /// This is a signal to the connection to halt all operations.
50    InvalidState(InvalidState),
51    /// We could not parse the display string.
52    CouldntParseDisplay {
53        /// True if we got the display string from an environment
54        /// variable, false otherwise.
55        from_env: bool,
56    },
57    /// This type was poisoned.
58    Poisoned {
59        /// The name of the involved type.
60        type_name: &'static str,
61    },
62    /// We could not parse the given type.
63    ParseError(ParseError),
64    /// A failure occurred during the setup process.
65    SetupFailed(SetupFailure),
66    /// An X11 error occurred.
67    X11Error(X11Error),
68    /// The extension is missing.
69    MissingExtension {
70        /// The name of the extension.
71        name: &'static str,
72    },
73    /// Request was too large to be sent.
74    RequestTooLarge {
75        /// Length of the request.
76        x_len: usize,
77        /// Maximum request length.
78        max_len: usize,
79    },
80    /// Attempted to send a request while another was in progress.
81    #[cfg(feature = "async")]
82    AsyncSendInProgress {
83        /// The sequence number of the request that was in progress.
84        seq: u64,
85    },
86    /// An I/O error occurred.
87    #[cfg(feature = "std")]
88    Io(IoError),
89}
90
91#[allow(dead_code)]
92#[derive(Clone, Copy)]
93pub(crate) enum Unsupported {
94    Fds,
95    Socket,
96}
97
98/// Reason why something entered an invalid state.
99///
100/// Keeping this crate private means that we can add more in the future
101/// if we want to.
102#[derive(Clone, Copy)]
103pub(crate) enum InvalidState {
104    UnexpectedFds,
105    NotEnoughSetup,
106    ScreenOutOfRange,
107    ZeroIdMask,
108    BadError,
109    XidsExhausted,
110}
111
112/// The setup for the connection failed.
113#[derive(Clone)]
114pub enum SetupFailure {
115    Failed(SetupFailed),
116    Authenticate(SetupAuthenticate),
117}
118
119// crate-private API
120impl Error {
121    fn from_inner(inner: Inner) -> Self {
122        Self {
123            inner,
124            initialization: false,
125        }
126    }
127
128    pub(crate) fn make_invalid_state(is: InvalidState) -> Self {
129        Error::from_inner(Inner::InvalidState(is))
130    }
131
132    #[allow(dead_code)]
133    pub(crate) fn make_unsupported(us: Unsupported) -> Self {
134        Error::from_inner(Inner::Unsupported(us))
135    }
136
137    pub(crate) fn couldnt_parse_display(from_env: bool) -> Self {
138        Error::from_inner(Inner::CouldntParseDisplay { from_env })
139    }
140
141    pub(crate) fn make_poisoned<T>() -> Self {
142        Error::from_inner(Inner::Poisoned {
143            type_name: type_name::<T>(),
144        })
145    }
146
147    pub(crate) fn make_large_request(x_len: usize, max_len: usize) -> Self {
148        Error::from_inner(Inner::RequestTooLarge { x_len, max_len })
149    }
150
151    pub(crate) fn make_setup_failure(sf: SetupFailure) -> Self {
152        Error::from_inner(Inner::SetupFailed(sf))
153    }
154
155    pub(crate) fn make_connect_error(ce: ConnectError) -> Self {
156        // convert the error to one of our other variants
157        let mut err = match ce {
158            ConnectError::ParseError(pe) => Error::make_parse_error(pe),
159            #[cfg(feature = "std")]
160            ConnectError::IoError(io) => Error::io(io),
161            ConnectError::InvalidScreen => {
162                Error::make_invalid_state(InvalidState::ScreenOutOfRange)
163            }
164            ConnectError::ZeroIdMask => Error::make_invalid_state(InvalidState::ZeroIdMask),
165            ConnectError::SetupFailed(sf) => Error::make_setup_failure(SetupFailure::Failed(sf)),
166            ConnectError::SetupAuthenticate(sa) => {
167                Error::make_setup_failure(SetupFailure::Authenticate(sa))
168            }
169            ConnectError::DisplayParsingError => Error::couldnt_parse_display(false),
170            ConnectError::Incomplete { .. } => {
171                Error::make_invalid_state(InvalidState::NotEnoughSetup)
172            }
173            _ => unreachable!(),
174        };
175
176        err.initialization = true;
177        err
178    }
179
180    /// Tell if this is a would-block I/O error.
181    pub(crate) fn would_block(&self) -> bool {
182        cfg_if::cfg_if! {
183            if #[cfg(feature = "std")] {
184                match self.as_io_error() {
185                    Some(io) => io.kind() == ErrorKind::WouldBlock,
186                    None => false,
187                }
188            } else {
189                false
190            }
191        }
192    }
193
194    #[allow(dead_code)]
195    pub(crate) fn is_protocol_error(&self) -> bool {
196        matches!(
197            self.inner,
198            Inner::X11Error(..) | Inner::MissingExtension { .. }
199        )
200    }
201}
202
203cfg_std! {
204    impl Error {
205        pub(crate) fn io(io: IoError) -> Self {
206            if !matches!(io.kind(), ErrorKind::WouldBlock) {
207                tracing::error!("encountered I/O error: {io:?}", io = io);
208            }
209            Error::from_inner(Inner::Io(io))
210        }
211    }
212}
213
214// public API
215impl Error {
216    /// Create a new error from something that can be formatted into
217    /// a message.
218    #[must_use]
219    pub fn make_msg<D: fmt::Display>(msg: D) -> Self {
220        Self::from_inner(Inner::Message(msg.to_string()))
221    }
222
223    /// Did this error happen as the result of calling an
224    /// unsupported operation?
225    #[must_use]
226    pub fn unsupported(&self) -> bool {
227        matches!(self.inner, Inner::Unsupported(_))
228    }
229
230    /// Create an error from an X11 parse error.
231    #[must_use]
232    pub fn make_parse_error(pe: ParseError) -> Self {
233        Error::from_inner(Inner::ParseError(pe))
234    }
235
236    /// Create an error from a missing extension.
237    #[must_use]
238    pub fn make_missing_extension(name: &'static str) -> Self {
239        Error::from_inner(Inner::MissingExtension { name })
240    }
241
242    /// Did this error happen as a result of some state-based
243    /// object entering an invalid state?
244    #[must_use]
245    pub fn invalid_state(&self) -> bool {
246        #[allow(unused_mut)]
247        let mut base = matches!(self.inner, Inner::InvalidState(_) | Inner::Poisoned { .. });
248
249        cfg_if::cfg_if! {
250            if #[cfg(feature = "std")] {
251                base |= matches!(self.inner, Inner::Io(ref err) if err.kind() != ErrorKind::WouldBlock);
252            }
253        }
254
255        base
256    }
257
258    /// Did this error occur during initialization of the X11
259    /// connection?
260    #[must_use]
261    pub fn initialization(&self) -> bool {
262        self.initialization
263    }
264
265    /// Get the inner setup failure that this error is a wrapper around.
266    #[must_use]
267    pub fn as_setup_failure(&self) -> Option<&SetupFailure> {
268        match self.inner {
269            Inner::SetupFailed(ref sf) => Some(sf),
270            _ => None,
271        }
272    }
273
274    /// Convert this error into a setup failure.
275    pub fn into_setup_failure(self) -> Result<SetupFailure> {
276        match self.inner {
277            Inner::SetupFailed(sf) => Ok(sf),
278            inner => Err(Self::from_inner(inner)),
279        }
280    }
281}
282
283// crate-private api
284cfg_async! {
285    impl Error {
286        pub(crate) fn async_send_in_progress(seq: u64) -> Self {
287            Error::from_inner(Inner::AsyncSendInProgress { seq })
288        }
289    }
290}
291
292// public API
293cfg_std! {
294    impl Error {
295        /// Get the inner I/O error that this error is a wrapper around.
296        #[must_use]
297        pub fn as_io_error(&self) -> Option<&IoError> {
298            match self.inner {
299                Inner::Io(ref io) => Some(io),
300                _ => None,
301            }
302        }
303
304        /// Convert this error into an I/O error.
305        pub fn into_io_error(self) -> core::result::Result<IoError, Self> {
306            match self.inner {
307                Inner::Io(io) => Ok(io),
308                inner => Err(Self::from_inner(inner)),
309            }
310        }
311    }
312}
313
314// crate-private API
315cfg_std_unix! {
316    impl Error {
317        pub(crate) fn nix(errno: Errno) -> Self {
318            Error::io(IoError::from_raw_os_error(errno as i32))
319        }
320    }
321}
322
323/* public trait impls */
324
325impl fmt::Debug for Error {
326    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
327        struct InnerRepr<'a>(&'a Inner);
328
329        impl<'a> fmt::Debug for InnerRepr<'a> {
330            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
331                match self.0 {
332                    Inner::Unsupported(ref e) => write!(f, "Unsupported: {}", e),
333                    Inner::Message(ref msg) => f.write_str(msg),
334                    Inner::InvalidState(ref e) => write!(f, "InvalidState: {}", e),
335                    Inner::CouldntParseDisplay { .. } => f.write_str("CouldntParseDisplay"),
336                    Inner::Poisoned { type_name } => write!(f, "Poisoned({})", type_name),
337                    Inner::ParseError(err) => write!(f, "ParseError: {}", err),
338                    Inner::SetupFailed(SetupFailure::Authenticate(_)) => {
339                        f.write_str("SetupFailed: could not authenticate")
340                    }
341                    Inner::SetupFailed(SetupFailure::Failed(fail)) => {
342                        let reason = str::from_utf8(&fail.reason).unwrap_or("bad utf-8");
343                        write!(f, "SetupFailed: {}", reason)
344                    }
345                    Inner::X11Error(x11) => fmt::Debug::fmt(x11, f),
346                    Inner::MissingExtension { name } => {
347                        write!(f, "MissingExtension: {}", name)
348                    }
349                    Inner::RequestTooLarge { x_len, max_len } => {
350                        write!(f, "RequestTooLarge: {} > {}", x_len, max_len)
351                    }
352                    #[cfg(feature = "async")]
353                    Inner::AsyncSendInProgress { seq } => {
354                        write!(f, "AsyncSendInProgress: seq {}", seq)
355                    }
356                    #[cfg(feature = "std")]
357                    Inner::Io(ref e) => write!(f, "{:?}", e),
358                }
359            }
360        }
361
362        f.debug_tuple("Error")
363            .field(&InnerRepr(&self.inner))
364            .finish()
365    }
366}
367
368impl fmt::Display for Error {
369    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
370        match self.inner {
371            Inner::Unsupported(ref e) => write!(f, "attempted an unsupported operation: {}", e),
372            Inner::InvalidState(ref e) => write!(f, "entered an invalid state: {}", e),
373            Inner::CouldntParseDisplay { from_env: true } => {
374                f.write_str("could not parse the $DISPLAY environment variable")
375            }
376            Inner::Poisoned { type_name } => write!(f, "object of type {} poisoned", type_name),
377            Inner::CouldntParseDisplay { from_env: false } => {
378                f.write_str("could not parse the display string")
379            }
380            Inner::ParseError(ref err) => fmt::Display::fmt(err, f),
381            Inner::SetupFailed(SetupFailure::Authenticate(ref e)) => {
382                let reason = str::from_utf8(&e.reason).unwrap_or("<invalid utf8>");
383                write!(f, "could not authenticate to the X server: {}", reason)
384            }
385            Inner::SetupFailed(SetupFailure::Failed(ref e)) => {
386                let reason = str::from_utf8(&e.reason).unwrap_or("<invalid utf8>");
387                write!(f, "could not setup the X connection: {}", reason)
388            }
389            Inner::X11Error(ref x11) => {
390                write!(
391                    f,
392                    "a {:?} error occurred on sequence number {}",
393                    x11.error_kind, x11.sequence,
394                )
395            }
396            Inner::MissingExtension { name } => {
397                write!(f, "missing extension: {}", name)
398            }
399            Inner::Message(ref msg) => f.write_str(msg),
400            Inner::RequestTooLarge { x_len, max_len } => {
401                write!(
402                    f,
403                    "Request of size {} bytes exceeds maximum length of {} bytes",
404                    x_len * 4,
405                    max_len * 4
406                )
407            }
408            #[cfg(feature = "async")]
409            Inner::AsyncSendInProgress { seq } => {
410                write!(f, "async send in progress for sequence {}", seq)
411            }
412            #[cfg(feature = "std")]
413            Inner::Io(ref e) => write!(f, "{}", e),
414        }
415    }
416}
417
418impl fmt::Display for Unsupported {
419    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
420        match *self {
421            Unsupported::Fds => f.write_str("file descriptors"),
422            Unsupported::Socket => f.write_str("unix sockets"),
423        }
424    }
425}
426
427impl fmt::Display for InvalidState {
428    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
429        match *self {
430            InvalidState::UnexpectedFds => f.write_str("unexpected file descriptors"),
431            InvalidState::NotEnoughSetup => f.write_str("not enough data for setup"),
432            InvalidState::ScreenOutOfRange => f.write_str("screen out of range"),
433            InvalidState::ZeroIdMask => f.write_str("zero id mask"),
434            InvalidState::BadError => f.write_str("misformatted error"),
435            InvalidState::XidsExhausted => f.write_str("server ran out of xids"),
436        }
437    }
438}
439
440// public api
441impl From<X11Error> for Error {
442    fn from(x11: X11Error) -> Self {
443        Self::from_inner(Inner::X11Error(x11))
444    }
445}
446
447cfg_std! {
448    impl From<IoError> for Error {
449        fn from(e: IoError) -> Self {
450            Self::from_inner(Inner::Io(e))
451        }
452    }
453
454    impl StdError for Error {
455        fn source(&self) -> Option<&(dyn StdError + 'static)> {
456            match self.inner {
457                Inner::ParseError(ref pe) => Some(pe),
458                #[cfg(feature = "std")]
459                Inner::Io(ref e) => Some(e),
460                _ => None,
461            }
462        }
463    }
464}
465
466/// Indicates that any errors that occur during execution of this function
467/// are initialization errors.
468pub(crate) fn initialization<R>(f: impl FnOnce() -> Result<R>) -> Result<R> {
469    match f() {
470        Ok(r) => Ok(r),
471        Err(mut e) => {
472            e.initialization = true;
473            Err(e)
474        }
475    }
476}
477
478/// A convenience type that is equivalent to
479/// `Result<T, [Error]>`.
480pub type Result<T = ()> = core::result::Result<T, Error>;