Skip to main content

psrp_rs/
error.rs

1//! Error type for the `psrp-rs` crate.
2
3use thiserror::Error;
4
5/// Errors raised by `psrp-rs` operations.
6///
7/// Transport-level failures from `winrm-rs` are wrapped via `From<WinrmError>`.
8#[derive(Debug, Error)]
9pub enum PsrpError {
10    /// Transport-level error from `winrm-rs`.
11    #[error(transparent)]
12    Winrm(#[from] winrm_rs::WinrmError),
13
14    /// PSRP protocol-level error (unexpected message, bad state transition, …).
15    #[error("PSRP protocol: {0}")]
16    Protocol(String),
17
18    /// CLIXML parse / encode failure.
19    #[error("CLIXML: {0}")]
20    Clixml(String),
21
22    /// Fragment reassembly failure (truncated header, inconsistent blob length, …).
23    #[error("fragment reassembly: {0}")]
24    Fragment(String),
25
26    /// The runspace pool was in the wrong state for the requested operation.
27    #[error("runspace pool not in state {expected}, got {actual}")]
28    BadState {
29        /// The state that was expected.
30        expected: String,
31        /// The state that was actually observed.
32        actual: String,
33    },
34
35    /// The pipeline was stopped by the caller or by the server.
36    #[error("pipeline stopped")]
37    Stopped,
38
39    /// The pipeline failed on the server side (a `PipelineState=Failed` message).
40    #[error("pipeline failed: {0}")]
41    PipelineFailed(String),
42
43    /// An operation was cancelled.
44    #[error("operation cancelled")]
45    Cancelled,
46}
47
48impl PsrpError {
49    pub(crate) fn protocol(msg: impl Into<String>) -> Self {
50        Self::Protocol(msg.into())
51    }
52
53    pub(crate) fn clixml(msg: impl Into<String>) -> Self {
54        Self::Clixml(msg.into())
55    }
56
57    pub(crate) fn fragment(msg: impl Into<String>) -> Self {
58        Self::Fragment(msg.into())
59    }
60}
61
62/// Convenience alias.
63pub type Result<T> = std::result::Result<T, PsrpError>;
64
65#[cfg(test)]
66mod tests {
67    use super::*;
68
69    #[test]
70    fn display_variants() {
71        assert!(
72            PsrpError::protocol("x")
73                .to_string()
74                .contains("PSRP protocol")
75        );
76        assert!(PsrpError::clixml("x").to_string().contains("CLIXML"));
77        assert!(
78            PsrpError::fragment("x")
79                .to_string()
80                .contains("fragment reassembly")
81        );
82        assert!(
83            PsrpError::BadState {
84                expected: "Opened".into(),
85                actual: "Opening".into(),
86            }
87            .to_string()
88            .contains("Opened")
89        );
90        assert_eq!(PsrpError::Stopped.to_string(), "pipeline stopped");
91        assert!(
92            PsrpError::PipelineFailed("boom".into())
93                .to_string()
94                .contains("boom")
95        );
96        assert_eq!(PsrpError::Cancelled.to_string(), "operation cancelled");
97    }
98
99    #[test]
100    fn from_winrm_error() {
101        let we = winrm_rs::WinrmError::Timeout(5);
102        let pe: PsrpError = we.into();
103        assert!(matches!(pe, PsrpError::Winrm(_)));
104    }
105}