Skip to main content

conpty_oxide/
status.rs

1// SPDX-FileCopyrightText: 2026 conpty-oxide contributors <https://github.com/P4suta/conpty-oxide/graphs/contributors>
2//
3// SPDX-License-Identifier: MIT OR Apache-2.0
4
5//! Child process exit status.
6//!
7//! Windows exit codes are plain `DWORD`s: there are no signals and no
8//! "terminated by" case, so [`ExitStatus`] is a thin, always-valid wrapper
9//! around a `u32` rather than the platform-abstracting enum a cross-platform
10//! API would need. The type lives outside the front-end modules because the
11//! blocking and async APIs both hand it back from `wait`.
12
13use core::fmt;
14
15/// The exit status of a child process that has terminated.
16///
17/// Obtained from either front end's `Session::wait`, `Session::try_wait`,
18/// `SessionOutput::status`, or the lower-level `Child::wait` and
19/// `Child::try_wait`. The wrapped value is exactly what `GetExitCodeProcess`
20/// reported, read only after the process handle was confirmed signaled — so
21/// it can never be the `STILL_ACTIVE` sentinel of a still-running process.
22///
23/// # Examples
24///
25/// ```
26/// use conpty_oxide::ExitStatus;
27///
28/// # fn check(status: ExitStatus) {
29/// if status.success() {
30///     println!("clean exit");
31/// } else {
32///     println!("failed with {}", status.code());
33/// }
34/// # }
35/// ```
36#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
37pub struct ExitStatus(u32);
38
39impl ExitStatus {
40    /// Wraps a raw exit code obtained from `GetExitCodeProcess`.
41    #[must_use]
42    #[cfg(any(feature = "blocking", feature = "tokio", test))]
43    pub(super) const fn from_raw(code: u32) -> Self {
44        Self(code)
45    }
46
47    /// Returns the raw exit code.
48    ///
49    /// Unlike `std::process::ExitStatus::code` this is not an [`Option`]: a
50    /// Windows process always has an exit code, including when it was
51    /// terminated by `Child::kill`.
52    #[must_use]
53    pub const fn code(&self) -> u32 {
54        self.0
55    }
56
57    /// Returns whether the process exited with code `0`.
58    #[must_use]
59    pub const fn success(&self) -> bool {
60        self.0 == 0
61    }
62}
63
64/// Formats as `exit code: <code>`, matching `std::process::ExitStatus` on
65/// Windows: decimal for ordinary codes, hexadecimal when the high bit is set.
66///
67/// The hexadecimal case matters more here than it would elsewhere, because
68/// the code this crate documents most — `STATUS_CONTROL_C_EXIT`, reported by
69/// a child whose terminal went away — is in that range: it renders as
70/// `exit code: 0xc000013a`, the spelling `NTSTATUS` values are written in
71/// everywhere, rather than the unrecognizable `3221225786`.
72impl fmt::Display for ExitStatus {
73    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
74        if self.0 & 0x8000_0000 != 0 {
75            write!(f, "exit code: {:#x}", self.0)
76        } else {
77            write!(f, "exit code: {}", self.0)
78        }
79    }
80}
81
82#[cfg(test)]
83#[path = "status_tests.rs"]
84mod tests;