Skip to main content

win_error_codes/
lstatus.rs

1// Copyright (c) Mike Grier.
2
3use std::fmt;
4
5use crate::Win32Error;
6
7#[cfg(test)]
8mod tests;
9
10/// A Win32 error code in a signed carrier: the `LONG` that `LSTATUS` is, which the registry calls
11/// (`RegOpenKeyExW` and the rest) return.
12///
13/// It is the same number space as [`Win32Error`], and the registry's own documentation calls the
14/// value an error code: zero is `ERROR_SUCCESS` and anything else is the code. It is a type of its
15/// own because it is the signed carrier, so a `LONG` is never read as a `DWORD` by accident, and
16/// the cast between the two is made once, here, and is a bit cast that loses nothing in either
17/// direction. A value is not assumed to be non-negative: a code above 2^31 is a negative `LONG`.
18///
19/// # Example
20///
21/// ```
22/// use win_error_codes::{LStatus, Win32Error};
23///
24/// // What a registry call returns for success, and for ERROR_FILE_NOT_FOUND.
25/// assert_eq!(LStatus::from_raw(0).into_result(), Ok(()));
26/// assert_eq!(
27///     LStatus::from_raw(2).into_result(),
28///     Err(Win32Error::from_code(2))
29/// );
30///
31/// // The same number space, so the two convert both ways without loss, including a code that
32/// // needs the sign bit.
33/// let status = LStatus::from_raw(-1_069_940_715);
34/// assert_eq!(status.to_win32(), Win32Error::from_code(0xC03A_0015));
35/// assert_eq!(Win32Error::from_code(0xC03A_0015).to_lstatus(), status);
36/// ```
37#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
38pub struct LStatus(i32);
39
40impl LStatus {
41    /// Wraps the raw `LONG` a call returned. Any value is accepted, zero included.
42    #[must_use]
43    pub const fn from_raw(raw: i32) -> Self {
44        Self(raw)
45    }
46
47    /// The raw `LONG`, unaltered.
48    #[must_use]
49    pub const fn raw(self) -> i32 {
50        self.0
51    }
52
53    /// Whether this is `ERROR_SUCCESS`, which is zero.
54    #[must_use]
55    pub const fn is_success(self) -> bool {
56        self.to_win32().is_success()
57    }
58
59    /// `Ok` for `ERROR_SUCCESS` and `Err` carrying the code for anything else, which is how a call
60    /// that returns an `LSTATUS` becomes a `Result`.
61    ///
62    /// # Errors
63    ///
64    /// The code, as a [`Win32Error`], when it is not `ERROR_SUCCESS`.
65    pub const fn into_result(self) -> Result<(), Win32Error> {
66        self.to_win32().into_result()
67    }
68
69    /// The same code as a [`Win32Error`]: a bit cast from `i32` to `u32`, so a negative value is a
70    /// code above 2^31 and not its magnitude.
71    #[must_use]
72    pub const fn to_win32(self) -> Win32Error {
73        Win32Error::from_code(self.0.cast_unsigned())
74    }
75}
76
77impl Win32Error {
78    /// The same code as an [`LStatus`]: a bit cast from `u32` to `i32`, so a code above 2^31 is a
79    /// negative value, which is how a `LONG` carries it.
80    #[must_use]
81    pub const fn to_lstatus(self) -> LStatus {
82        LStatus::from_raw(self.code().cast_signed())
83    }
84}
85
86impl From<LStatus> for Win32Error {
87    fn from(status: LStatus) -> Self {
88        status.to_win32()
89    }
90}
91
92impl From<Win32Error> for LStatus {
93    fn from(error: Win32Error) -> Self {
94        error.to_lstatus()
95    }
96}
97
98/// The code as [`Win32Error`] shows it: there is no separate rendering for the signed carrier,
99/// since the two are the same number space.
100impl fmt::Display for LStatus {
101    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
102        fmt::Display::fmt(&self.to_win32(), f)
103    }
104}