Skip to main content

windows_namespace_request_sys/
outcome.rs

1// Copyright (c) Mike Grier.
2
3//! The faithful-execution contract every entry follows.
4//!
5//! An entry reports what Windows reported. It does not normalise a code, map it
6//! onto a friendlier taxonomy, or decide that one failure "really means"
7//! something else.
8//!
9//! # Why preservation is a constraint, not a preference
10//!
11//! `ERROR_FILE_NOT_FOUND` means three different things depending on which call
12//! produced it and when: a missing directory from an open, an **empty**
13//! directory from a first query, and a genuine failure from a later one. Only a
14//! consumer holding that context can tell them apart. Any reclassification here
15//! destroys information no layer above can reconstruct.
16//!
17//! # Why the code is snapshotted rather than read later
18//!
19//! `GetLastError` is thread state, and it is *volatile* thread state: almost
20//! any subsequent Win32 call overwrites it, including cleanup a caller does not
21//! think of as a call at all -- a `CloseHandle` in a `Drop`, a buffer being
22//! released, a restoration guard unwinding. Reading it a few statements after
23//! the failure is a race against the entry's own tidying up.
24//!
25//! So the read is not left to the caller's discipline. [`perform`] and its
26//! convention-specific forms take the call as a closure and snapshot the code
27//! **in the statement after it returns**, before anything else can run. Binding
28//! to these functions is what makes the guarantee structural rather than a rule
29//! each entry has to remember.
30//!
31//! # Scope: entries, not capture
32//!
33//! This governs the Win32 call an entry *performs*. It does not govern capture
34//! failures -- [`crate::handle`], [`crate::security`], and [`crate::path`]
35//! report a named stage plus a code, because there the useful question is which
36//! part of building the request went wrong. Those happen on the calling thread,
37//! before any entry runs.
38
39use std::fmt;
40use std::io;
41
42use windows_sys::Win32::Foundation::{
43    FALSE, GetLastError, HANDLE, INVALID_HANDLE_VALUE, WIN32_ERROR,
44};
45
46/// A raw Win32 error code, exactly as Windows produced it.
47///
48/// Deliberately not an enum: the point of this type is that it carries whatever
49/// Windows said, including codes this crate has never heard of.
50///
51/// # Example
52///
53/// ```
54/// use windows_namespace_request_sys::Win32Error;
55/// use windows_sys::Win32::Foundation::ERROR_FILE_NOT_FOUND;
56///
57/// let error = Win32Error::from_code(ERROR_FILE_NOT_FOUND);
58/// assert_eq!(error.code(), ERROR_FILE_NOT_FOUND);
59///
60/// // The io::Error form is a re-presentation, not a reclassification: the raw
61/// // code survives it.
62/// assert_eq!(error.to_io_error().raw_os_error(), Some(ERROR_FILE_NOT_FOUND as i32));
63///
64/// // A code this crate has never heard of is carried just the same.
65/// let unknown = Win32Error::from_code(0x0BAD_F00D);
66/// assert_eq!(unknown.code(), 0x0BAD_F00D);
67/// ```
68#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
69pub struct Win32Error(WIN32_ERROR);
70
71impl Win32Error {
72    /// Wraps a raw `WIN32_ERROR`.
73    #[must_use]
74    pub fn from_code(code: WIN32_ERROR) -> Self {
75        Self(code)
76    }
77
78    /// Snapshots the calling thread's last error.
79    ///
80    /// Call this only immediately after the failing Win32 call. Prefer
81    /// [`perform`], which makes that ordering structural.
82    fn last() -> Self {
83        // SAFETY: GetLastError only reads the calling thread's own error slot.
84        Self(unsafe { GetLastError() })
85    }
86
87    /// The raw code, unaltered.
88    #[must_use]
89    pub fn code(self) -> WIN32_ERROR {
90        self.0
91    }
92
93    /// The same failure as a standard [`io::Error`], for callers that funnel
94    /// everything through `std::io`.
95    ///
96    /// This is a re-presentation, not a reclassification: the raw code survives
97    /// as [`io::Error::raw_os_error`].
98    #[must_use]
99    pub fn to_io_error(self) -> io::Error {
100        i32::try_from(self.0).map_or_else(
101            |_| io::Error::other(format!("Win32 error {}", self.0)),
102            io::Error::from_raw_os_error,
103        )
104    }
105}
106
107impl fmt::Display for Win32Error {
108    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
109        write!(f, "Win32 error {}: {}", self.0, self.to_io_error())
110    }
111}
112
113impl std::error::Error for Win32Error {}
114
115/// What an entry's Win32 call produced: its result, or the raw code.
116pub type Outcome<T> = Result<T, Win32Error>;
117
118/// Performs `call` and, when `failed` says the result is a failure, snapshots
119/// the thread's last error before anything else can run.
120///
121/// `failed` decides using only the returned value, because Win32's failure
122/// conventions differ per call and none of them is inferable from the type. The
123/// three the catalogue actually meets have named forms:
124/// [`perform_bool`], [`perform_handle`], and [`perform_nonzero`]. Use this
125/// general form for a call whose convention is none of those.
126///
127/// # Example
128///
129/// The guarantee this function exists for. Cleanup between the failing call and
130/// the read is exactly what destroys a last-error code, and binding to
131/// `perform` closes that window:
132///
133/// ```
134/// use windows_namespace_request_sys::outcome::perform;
135/// use windows_sys::Win32::Foundation::{
136///     ERROR_ACCESS_DENIED, ERROR_FILE_NOT_FOUND, SetLastError,
137/// };
138///
139/// let outcome = perform(
140///     || {
141///         // SAFETY: SetLastError writes only this thread's error slot.
142///         unsafe { SetLastError(ERROR_FILE_NOT_FOUND) };
143///         -1_i32
144///     },
145///     |result| *result < 0,
146/// );
147///
148/// // Cleanup runs afterwards and clobbers the thread's error slot -- a Drop, a
149/// // buffer release, a restoration guard. The snapshot is already taken.
150/// // SAFETY: as above.
151/// unsafe { SetLastError(ERROR_ACCESS_DENIED) };
152///
153/// assert_eq!(
154///     outcome.expect_err("a negative result is a failure").code(),
155///     ERROR_FILE_NOT_FOUND
156/// );
157/// ```
158pub fn perform<T>(call: impl FnOnce() -> T, failed: impl FnOnce(&T) -> bool) -> Outcome<T> {
159    let result = call();
160
161    // Nothing may go between these two statements. No drop runs here, no
162    // cleanup, no second Win32 call -- which is the entire reason this function
163    // exists rather than each entry reading GetLastError for itself.
164    if failed(&result) {
165        return Err(Win32Error::last());
166    }
167
168    Ok(result)
169}
170
171/// Performs a call whose `BOOL` return is `FALSE` on failure.
172///
173/// The successful value carries no information beyond "it worked", so it is
174/// discarded rather than handed back as a bare integer.
175///
176/// # Errors
177///
178/// Returns the raw Win32 code when the call returns `FALSE`.
179pub fn perform_bool(call: impl FnOnce() -> i32) -> Outcome<()> {
180    perform(call, |result| *result == FALSE).map(|_| ())
181}
182
183/// Performs a call whose `HANDLE` return is `INVALID_HANDLE_VALUE` on failure.
184///
185/// This is the convention of `CreateFileW`, `OpenFileById`, and
186/// `FindFirstChangeNotificationW`.
187///
188/// # Errors
189///
190/// Returns the raw Win32 code when the call returns `INVALID_HANDLE_VALUE`.
191///
192/// # Example
193///
194/// The two handle conventions disagree about the same values, which is why both
195/// exist by name. Using one where the other belongs turns a failure into a
196/// plausible-looking handle:
197///
198/// ```
199/// use std::ptr;
200///
201/// use windows_namespace_request_sys::outcome::{perform_handle, perform_nonnull_handle};
202/// use windows_sys::Win32::Foundation::INVALID_HANDLE_VALUE;
203///
204/// // Under the INVALID_HANDLE_VALUE convention, null is a *success*.
205/// assert!(perform_handle(ptr::null_mut).is_ok());
206/// assert!(perform_handle(|| INVALID_HANDLE_VALUE).is_err());
207///
208/// // Under the null convention, the two swap.
209/// assert!(perform_nonnull_handle(|| INVALID_HANDLE_VALUE).is_ok());
210/// assert!(perform_nonnull_handle(ptr::null_mut).is_err());
211/// ```
212pub fn perform_handle(call: impl FnOnce() -> HANDLE) -> Outcome<HANDLE> {
213    perform(call, |result| *result == INVALID_HANDLE_VALUE)
214}
215
216/// Performs a call whose `HANDLE` return is **null** on failure.
217///
218/// A distinct convention from [`perform_handle`], and getting the two the wrong
219/// way round turns a failure into a plausible-looking handle. Both are provided
220/// because Windows uses both.
221///
222/// # Errors
223///
224/// Returns the raw Win32 code when the call returns a null handle.
225pub fn perform_nonnull_handle(call: impl FnOnce() -> HANDLE) -> Outcome<HANDLE> {
226    perform(call, |result| result.is_null())
227}
228
229/// Performs a call whose numeric return is `0` on failure.
230///
231/// This is the convention of the sizing and length calls, such as
232/// `GetFullPathNameW` and `GetFinalPathNameByHandleW`.
233///
234/// # Errors
235///
236/// Returns the raw Win32 code when the call returns `0`.
237pub fn perform_nonzero(call: impl FnOnce() -> u32) -> Outcome<u32> {
238    perform(call, |result| *result == 0)
239}
240
241#[cfg(test)]
242mod tests;