Skip to main content

tympan_apo/
error.rs

1//! `HRESULT` wrapper and APO-specific error constants.
2//!
3//! COM uses the 32-bit `HRESULT` value as its universal success /
4//! failure indicator. Negative values are failures, non-negative
5//! values are successes (with `S_OK` and `S_FALSE` being the two
6//! common ones). The audio engine extends this with a set of APO
7//! facility codes (`FACILITY_AUDIO`) documented in
8//! `audioenginebaseapo.h`.
9//!
10//! `HResult` is a thin `#[repr(transparent)]` wrapper so it can be
11//! used interchangeably with the raw `i32` at the FFI boundary,
12//! including in `extern "system"` return positions.
13
14use core::fmt;
15
16/// COM `HRESULT`.
17///
18/// Layout-compatible with `i32` and with the `windows-core` crate's
19/// `HRESULT(pub i32)` type. Conversions to and from
20/// `windows_core::HRESULT` are provided on Windows.
21#[derive(Copy, Clone, PartialEq, Eq, Hash)]
22#[repr(transparent)]
23pub struct HResult(pub i32);
24
25impl HResult {
26    /// `S_OK` — success, no further information.
27    pub const S_OK: Self = Self(0);
28    /// `S_FALSE` — success with a "false" semantic (e.g. operation
29    /// completed but the queried condition does not hold).
30    pub const S_FALSE: Self = Self(1);
31
32    /// `E_NOTIMPL` — function is not implemented.
33    pub const E_NOTIMPL: Self = Self(0x8000_4001_u32 as i32);
34    /// `E_NOINTERFACE` — `QueryInterface` could not produce the
35    /// requested interface.
36    pub const E_NOINTERFACE: Self = Self(0x8000_4002_u32 as i32);
37    /// `E_POINTER` — invalid (typically null) pointer argument.
38    pub const E_POINTER: Self = Self(0x8000_4003_u32 as i32);
39    /// `E_FAIL` — unspecified failure.
40    pub const E_FAIL: Self = Self(0x8000_4005_u32 as i32);
41    /// `E_UNEXPECTED` — catastrophic failure (last-resort code).
42    pub const E_UNEXPECTED: Self = Self(0x8000_FFFF_u32 as i32);
43    /// `E_OUTOFMEMORY` — allocation failure.
44    pub const E_OUTOFMEMORY: Self = Self(0x8007_000E_u32 as i32);
45    /// `E_INVALIDARG` — one or more arguments are invalid.
46    pub const E_INVALIDARG: Self = Self(0x8007_0057_u32 as i32);
47
48    /// `CLASS_E_CLASSNOTAVAILABLE` — `DllGetClassObject` does not
49    /// recognise the requested CLSID.
50    pub const CLASS_E_CLASSNOTAVAILABLE: Self = Self(0x8004_0111_u32 as i32);
51    /// `CLASS_E_NOAGGREGATION` — class refuses aggregation.
52    pub const CLASS_E_NOAGGREGATION: Self = Self(0x8004_0110_u32 as i32);
53
54    /// `APOERR_INVALID_INPUT_DATA` — input data does not match the
55    /// format negotiated during `LockForProcess`.
56    pub const APOERR_INVALID_INPUT_DATA: Self = Self(0x8889_0001_u32 as i32);
57    /// `APOERR_FORMAT_NOT_SUPPORTED` — proposed format is not
58    /// supported by this APO.
59    pub const APOERR_FORMAT_NOT_SUPPORTED: Self = Self(0x8889_0008_u32 as i32);
60    /// `APOERR_INVALID_API_VERSION` — caller is requesting an
61    /// unsupported APO API revision.
62    pub const APOERR_INVALID_API_VERSION: Self = Self(0x8889_0007_u32 as i32);
63    /// `APOERR_NUM_CONNECTIONS_INVALID` — the number of input or
64    /// output connections is not supported.
65    pub const APOERR_NUM_CONNECTIONS_INVALID: Self = Self(0x8889_000B_u32 as i32);
66    /// `APOERR_NOT_LOCKED` — operation is only valid between
67    /// `LockForProcess` and `UnlockForProcess`.
68    pub const APOERR_NOT_LOCKED: Self = Self(0x8889_000A_u32 as i32);
69    /// `APOERR_ALREADY_LOCKED` — APO is already locked.
70    pub const APOERR_ALREADY_LOCKED: Self = Self(0x8889_0006_u32 as i32);
71
72    /// Returns `true` if the underlying value is non-negative.
73    #[inline]
74    #[must_use]
75    pub const fn is_ok(self) -> bool {
76        self.0 >= 0
77    }
78
79    /// Returns `true` if the underlying value is negative.
80    #[inline]
81    #[must_use]
82    pub const fn is_err(self) -> bool {
83        self.0 < 0
84    }
85
86    /// Converts to a `Result<(), Self>`, mapping the success codes
87    /// (`S_OK`, `S_FALSE`, and any other non-negative value) to `Ok`.
88    #[inline]
89    pub const fn ok(self) -> Result<(), Self> {
90        if self.is_ok() {
91            Ok(())
92        } else {
93            Err(self)
94        }
95    }
96}
97
98impl fmt::Debug for HResult {
99    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
100        write!(f, "HResult(0x{:08X})", self.0 as u32)
101    }
102}
103
104impl fmt::Display for HResult {
105    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
106        write!(f, "HRESULT 0x{:08X}", self.0 as u32)
107    }
108}
109
110impl From<i32> for HResult {
111    #[inline]
112    fn from(value: i32) -> Self {
113        Self(value)
114    }
115}
116
117impl From<HResult> for i32 {
118    #[inline]
119    fn from(value: HResult) -> Self {
120        value.0
121    }
122}
123
124#[cfg(windows)]
125impl From<windows_core::HRESULT> for HResult {
126    #[inline]
127    fn from(value: windows_core::HRESULT) -> Self {
128        Self(value.0)
129    }
130}
131
132#[cfg(windows)]
133impl From<HResult> for windows_core::HRESULT {
134    #[inline]
135    fn from(value: HResult) -> Self {
136        Self(value.0)
137    }
138}
139
140#[cfg(test)]
141mod tests {
142    use super::*;
143
144    #[test]
145    fn success_codes_classify_correctly() {
146        assert!(HResult::S_OK.is_ok());
147        assert!(!HResult::S_OK.is_err());
148        assert!(HResult::S_FALSE.is_ok());
149        assert!(!HResult::S_FALSE.is_err());
150    }
151
152    #[test]
153    fn failure_codes_classify_correctly() {
154        assert!(HResult::E_FAIL.is_err());
155        assert!(!HResult::E_FAIL.is_ok());
156        assert!(HResult::E_INVALIDARG.is_err());
157        assert!(HResult::APOERR_FORMAT_NOT_SUPPORTED.is_err());
158        assert!(HResult::APOERR_INVALID_INPUT_DATA.is_err());
159        assert!(HResult::CLASS_E_CLASSNOTAVAILABLE.is_err());
160    }
161
162    #[test]
163    fn ok_method_converts_to_result() {
164        assert_eq!(HResult::S_OK.ok(), Ok(()));
165        assert_eq!(HResult::S_FALSE.ok(), Ok(()));
166        assert_eq!(HResult::E_FAIL.ok(), Err(HResult::E_FAIL));
167    }
168
169    #[test]
170    fn raw_constant_values_match_microsoft_definitions() {
171        // Sanity-check a handful of well-known constants against
172        // `winerror.h` / `audioenginebaseapo.h`.
173        assert_eq!(HResult::E_NOTIMPL.0 as u32, 0x8000_4001);
174        assert_eq!(HResult::E_POINTER.0 as u32, 0x8000_4003);
175        assert_eq!(HResult::E_OUTOFMEMORY.0 as u32, 0x8007_000E);
176        assert_eq!(HResult::E_INVALIDARG.0 as u32, 0x8007_0057);
177        assert_eq!(HResult::CLASS_E_CLASSNOTAVAILABLE.0 as u32, 0x8004_0111);
178    }
179
180    #[test]
181    fn debug_formatting_is_hex() {
182        let s = format!("{:?}", HResult::E_INVALIDARG);
183        assert_eq!(s, "HResult(0x80070057)");
184    }
185}