Skip to main content

agent_seat_linux/
error.rs

1//! Typed error shared by every fallible agent-seat operation.
2
3/// Every failure mode of the agent seat, from session discovery through
4/// capture and input injection.
5///
6/// Low-level APIs such as [`crate::AgentSeat`] and [`crate::SeatApp`] return
7/// this type directly; the high-level [`crate::Error`] wraps it and exposes
8/// it through [`std::error::Error::source`]. The `String` payloads carry
9/// free-form context while the variant identifies the failing subsystem.
10#[derive(Debug, thiserror::Error)]
11pub enum SeatError {
12    /// No Wayland session exists to proxy into: `WAYLAND_DISPLAY` is unset
13    /// or the compositor socket it names does not exist.
14    #[error("no Wayland session is available to proxy into")]
15    NoWaylandSession,
16
17    /// `XDG_RUNTIME_DIR` is unset or empty, so no socket path can be derived.
18    #[error("XDG_RUNTIME_DIR is not set; no runtime directory for the seat socket")]
19    MissingRuntimeDir,
20
21    /// The private agent seat socket could not be bound or configured.
22    #[error("could not create the agent seat socket: {0}")]
23    SocketCreate(String),
24
25    /// The XWayland compatibility bridge failed to start or serve.
26    #[error("XWayland bridge failure: {0}")]
27    Xwayland(String),
28
29    /// `xauth` credential setup for the XWayland bridge failed.
30    #[error("XWayland authority failure: {0}")]
31    Xauth(String),
32
33    /// Kernel peer credentials of a connecting app could not be read or did
34    /// not validate.
35    #[error("peer credential failure: {0}")]
36    PeerCredential(String),
37
38    /// Reading or decoding the app's rendered frame failed.
39    #[error("capture failure: {0}")]
40    Capture(String),
41
42    /// Synthesizing or delivering input events to the app failed.
43    #[error("input injection failure: {0}")]
44    Input(String),
45
46    /// Spawning or supervising a helper process or worker thread failed.
47    #[error("process failure: {0}")]
48    Process(String),
49
50    /// An underlying I/O operation failed.
51    #[error(transparent)]
52    Io(#[from] std::io::Error),
53
54    /// Any other failure that does not fit a dedicated variant.
55    #[error("{0}")]
56    Custom(String),
57}
58
59impl From<String> for SeatError {
60    fn from(message: String) -> Self {
61        Self::Custom(message)
62    }
63}
64
65impl From<&str> for SeatError {
66    fn from(message: &str) -> Self {
67        Self::Custom(message.to_string())
68    }
69}
70
71#[cfg(test)]
72mod tests {
73    use super::*;
74
75    #[test]
76    fn string_conversions_become_custom() {
77        let owned = SeatError::from(String::from("boom"));
78        assert!(matches!(&owned, SeatError::Custom(_)));
79        assert_eq!(owned.to_string(), "boom");
80
81        let borrowed = SeatError::from("boom");
82        assert!(matches!(&borrowed, SeatError::Custom(_)));
83        assert_eq!(borrowed.to_string(), "boom");
84    }
85
86    #[test]
87    fn io_errors_keep_their_message_and_chain() {
88        // A real source-bearing io::Error keeps its chain through `Io`.
89        let inner = std::io::Error::other("disk");
90        let has_source = std::error::Error::source(&inner).is_some();
91        let error = SeatError::from(inner);
92        assert!(matches!(&error, SeatError::Io(_)));
93        assert!(error.to_string().contains("disk"));
94        assert_eq!(std::error::Error::source(&error).is_some(), has_source);
95    }
96}