1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
//! Error traits and types

use runa_wayland_protocols::wayland::wl_display::v1 as wl_display;
use thiserror::Error;

use crate::objects::DISPLAY_ID;

/// Converting Rust errors to Wayland error events
pub trait ProtocolError: std::error::Error + Send + Sync + 'static {
    /// Returns an object id and a wayland error code associated with this
    /// error, in that order. If not None, an error event will be sent to
    /// the client.
    fn wayland_error(&self) -> Option<(u32, u32)>;
    /// Returns whether this error is fatal. If true, the client should be
    /// disconnected.
    fn fatal(&self) -> bool;
}

/// Common errors that can occur when handling client requests
#[derive(Error, Debug)]
pub enum Error {
    /// Failed to deserialize a message
    #[error("Deserialization error: {0}")]
    Deserialization(#[from] runa_io::traits::de::Error),
    /// I/O error
    #[error("I/O error: {0}")]
    Io(#[from] std::io::Error),
    /// Client tried to use an ID that is already in use
    #[error("NewId {0} sent by the client is already in use")]
    IdExists(u32),
    /// Client tried to use an object that's not suitable for the request
    #[error("Object {0} is invalid for this operation")]
    InvalidObject(u32),
    /// Client tried to use an unknown global
    #[error("Unknown Global: {0}")]
    UnknownGlobal(u32),
    /// Client tried to use an unknown object
    #[error("Unknown object: {0}")]
    UnknownObject(u32),
    /// Unknown error
    #[error("An unknown error occurred: {0}")]
    UnknownError(&'static str),
    /// Unknown error that is fatal
    #[error("An unknown fatal error occurred: {0}")]
    UnknownFatalError(&'static str),
    /// Client tried to invoke an unimplemented method
    #[error("{0} is not implemented yet")]
    NotImplemented(&'static str),
    /// Custom error defined by external object implementations
    #[error("{source}")]
    Custom {
        /// The source error
        #[source]
        source:             Box<dyn std::error::Error + Send + Sync + 'static>,
        /// The ID of the object that caused the error, and the wayland error
        /// code If not None, an error will be sent to the client.
        object_id_and_code: Option<(u32, u32)>,
        /// Whehther the client should be disconnected
        is_fatal:           bool,
    },
}

impl From<std::convert::Infallible> for Error {
    fn from(f: std::convert::Infallible) -> Self {
        match f {}
    }
}

impl From<Box<dyn std::error::Error + Send + Sync + 'static>> for Error {
    fn from(e: Box<dyn std::error::Error + Send + Sync + 'static>) -> Self {
        Self::Custom {
            object_id_and_code: Some((DISPLAY_ID, wl_display::enums::Error::Implementation as u32)),
            is_fatal:           true,
            source:             e,
        }
    }
}

impl Error {
    /// Create a new error from a custom error type
    pub fn custom<E>(e: E) -> Self
    where
        E: ProtocolError + Send + Sync + 'static,
    {
        Self::Custom {
            object_id_and_code: e.wayland_error(),
            is_fatal:           e.fatal(),
            source:             Box::new(e),
        }
    }
}
impl ProtocolError for Error {
    fn wayland_error(&self) -> Option<(u32, u32)> {
        use runa_wayland_protocols::wayland::wl_display::v1::enums::Error::*;
        match self {
            Self::Deserialization(_) => Some((DISPLAY_ID, InvalidMethod as u32)),
            Self::IdExists(_) => Some((DISPLAY_ID, InvalidObject as u32)),
            Self::UnknownGlobal(_) => Some((DISPLAY_ID, InvalidObject as u32)),
            Self::UnknownObject(_) => Some((DISPLAY_ID, InvalidObject as u32)),
            Self::InvalidObject(_) => Some((DISPLAY_ID, InvalidObject as u32)),
            Self::UnknownError(_) => Some((DISPLAY_ID, Implementation as u32)),
            Self::UnknownFatalError(_) => Some((DISPLAY_ID, Implementation as u32)),
            Self::NotImplemented(_) => Some((DISPLAY_ID, Implementation as u32)),
            Self::Custom {
                object_id_and_code, ..
            } => *object_id_and_code,
            Self::Io(_) => None, /* We had an I/O error communicating with the client, so we
                                  * better not send anything further */
        }
    }

    fn fatal(&self) -> bool {
        match self {
            Self::Deserialization(_) |
            Self::Io(_) |
            Self::IdExists(_) |
            Self::UnknownGlobal(_) |
            Self::UnknownObject(_) |
            Self::InvalidObject(_) |
            Self::UnknownFatalError(_) |
            Self::NotImplemented(_) => true,
            Self::UnknownError(_) => false,
            Self::Custom {
                is_fatal: fatal, ..
            } => *fatal,
        }
    }
}