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}