1+ //! Error handling types and traits for iron-remote-desktop.
2+ //!
3+ //! # Example: Handling RDCleanPath errors
4+ //!
5+ //! ```no_run
6+ //! # use iron_remote_desktop::*;
7+ //! # fn handle_error(error: impl IronError) {
8+ //! match error.kind() {
9+ //! IronErrorKind::RDCleanPath => {
10+ //! if let Some(details) = error.rdcleanpath_details() {
11+ //! // Check for specific HTTP errors
12+ //! if details.http_status_code() == Some(403) {
13+ //! // Handle forbidden/VNET deleted case
14+ //! }
15+ //! // Check for WSA errors
16+ //! if details.wsa_error_code() == Some(10013) {
17+ //! // Handle permission denied
18+ //! }
19+ //! }
20+ //! }
21+ //! _ => {}
22+ //! }
23+ //! # }
24+ //! ```
25+
126use wasm_bindgen:: prelude:: * ;
227
328pub trait IronError {
429 fn backtrace ( & self ) -> String ;
530
631 fn kind ( & self ) -> IronErrorKind ;
32+
33+ fn rdcleanpath_details ( & self ) -> Option < RDCleanPathDetails > ;
734}
835
936#[ derive( Clone , Copy ) ]
@@ -19,8 +46,83 @@ pub enum IronErrorKind {
1946 AccessDenied ,
2047 /// Something wrong happened when sending or receiving the RDCleanPath message
2148 RDCleanPath ,
22- /// Couldn’ t connect to proxy
49+ /// Couldn' t connect to proxy
2350 ProxyConnect ,
2451 /// Protocol negotiation failed
2552 NegotiationFailure ,
2653}
54+
55+ /// Detailed error information for RDCleanPath errors.
56+ ///
57+ /// When an RDCleanPath error occurs, this structure provides granular details
58+ /// about the underlying cause, including HTTP status codes, Windows Socket errors,
59+ /// and TLS alert codes.
60+ #[ derive( Clone , Copy , Debug ) ]
61+ #[ wasm_bindgen]
62+ pub struct RDCleanPathDetails {
63+ http_status_code : Option < u16 > ,
64+ wsa_error_code : Option < u16 > ,
65+ tls_alert_code : Option < u8 > ,
66+ }
67+
68+ // NOTE: multiple impl blocks required because wasm-bindgen doesn't support
69+ // non-exported constructors in #[wasm_bindgen] impl blocks
70+ #[ wasm_bindgen]
71+ impl RDCleanPathDetails {
72+ /// HTTP status code if the error originated from an HTTP response.
73+ ///
74+ /// Common values:
75+ /// - 403: Forbidden (e.g., deleted VNET, insufficient permissions)
76+ /// - 404: Not Found
77+ /// - 500: Internal Server Error
78+ /// - 502: Bad Gateway
79+ /// - 503: Service Unavailable
80+ #[ wasm_bindgen( getter, js_name = httpStatusCode) ]
81+ pub fn http_status_code ( & self ) -> Option < u16 > {
82+ self . http_status_code
83+ }
84+
85+ /// Windows Socket API (WSA) error code.
86+ ///
87+ /// Common values:
88+ /// - 10013: Permission denied (WSAEACCES) - often indicates deleted/invalid VNET
89+ /// - 10060: Connection timed out (WSAETIMEDOUT)
90+ /// - 10061: Connection refused (WSAECONNREFUSED)
91+ /// - 10051: Network is unreachable (WSAENETUNREACH)
92+ /// - 10065: No route to host (WSAEHOSTUNREACH)
93+ #[ wasm_bindgen( getter, js_name = wsaErrorCode) ]
94+ pub fn wsa_error_code ( & self ) -> Option < u16 > {
95+ self . wsa_error_code
96+ }
97+
98+ /// TLS alert code if the error occurred during TLS handshake.
99+ ///
100+ /// Common values:
101+ /// - 40: Handshake failure
102+ /// - 42: Bad certificate
103+ /// - 45: Certificate expired
104+ /// - 48: Unknown CA
105+ /// - 112: Unrecognized name
106+ #[ wasm_bindgen( getter, js_name = tlsAlertCode) ]
107+ pub fn tls_alert_code ( & self ) -> Option < u8 > {
108+ self . tls_alert_code
109+ }
110+ }
111+
112+ #[ expect(
113+ clippy:: allow_attributes,
114+ reason = "Unfortunately, expect attribute doesn't work with clippy::multiple_inherent_impl lint"
115+ ) ]
116+ #[ allow(
117+ clippy:: multiple_inherent_impl,
118+ reason = "We don't want to expose the constructor to JS"
119+ ) ]
120+ impl RDCleanPathDetails {
121+ pub fn new ( http_status_code : Option < u16 > , wsa_error_code : Option < u16 > , tls_alert_code : Option < u8 > ) -> Self {
122+ Self {
123+ http_status_code,
124+ wsa_error_code,
125+ tls_alert_code,
126+ }
127+ }
128+ }
0 commit comments