Skip to main content

zx_status/
lib.rs

1// Copyright 2018 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5//! Type-safe bindings for Zircon status.
6
7#![no_std]
8
9use core::fmt;
10use core::num::NonZero;
11
12pub mod sys {
13    pub use zx_types::{ZX_OK, zx_status_t};
14}
15
16macro_rules! assoc_values {
17    ($typename:ident, [$($(#[$attr:meta])* $name:ident = $value:path;)*]) => {
18        #[allow(non_upper_case_globals)]
19        impl $typename {
20            $(
21                $(#[$attr])*
22                pub const $name: $typename = match $typename::try_from_raw($value) {
23                    Some(val) => val,
24                    None => panic!("Status values must be non-zero"),
25                };
26            )*
27
28            const fn assoc_const_name(&self) -> Option<&'static str> {
29                match self.0.get() {
30                    $(
31                        $value => Some(stringify!($name)),
32                    )*
33                    _ => None,
34                }
35            }
36        }
37
38        impl ::core::fmt::Debug for $typename {
39            fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
40                f.write_str(concat!(stringify!($typename), "("))?;
41                match self.assoc_const_name() {
42                    Some(name) => f.write_str(&name)?,
43                    None => ::core::fmt::Debug::fmt(&self.0.get(), f)?,
44                }
45                f.write_str(")")
46            }
47        }
48    };
49}
50
51/// A non-zero Zircon status code representing an error.
52///
53/// Because this wraps a `core::num::NonZero<zx_types::zx_status_t>`, `Result<(), Status>`
54/// has a niche at `0` (`ZX_OK`), guaranteeing that `Result<(), Status>` has the exact same
55/// 4-byte memory layout and machine ABI as `zx_types::zx_status_t`.
56#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)]
57#[repr(transparent)]
58pub struct Status(NonZero<zx_types::zx_status_t>);
59
60impl Status {
61    /// Returns the symbolic name of the status (e.g. `"INVALID_ARGS"`), or `"UNKNOWN"` if
62    /// unrecognized.
63    #[inline]
64    pub const fn as_str(&self) -> &'static str {
65        match self.assoc_const_name() {
66            Some(name) => name,
67            None => "UNKNOWN",
68        }
69    }
70
71    /// Returns `Ok(())` if the status was `ZX_OK` (0),
72    /// otherwise returns `Err(status)`.
73    #[inline]
74    pub const fn ok(raw: zx_types::zx_status_t) -> Result<(), Status> {
75        match NonZero::new(raw) {
76            Some(err) => Err(Status(err)),
77            None => Ok(()),
78        }
79    }
80
81    /// Returns the raw `zx_status_t` code corresponding to a `Result<(), Status>`.
82    ///
83    /// Returns `ZX_OK` (`0`) for `Ok(())`, and the underlying error code for `Err(status)`.
84    #[inline]
85    pub const fn result_into_raw(res: Result<(), Self>) -> zx_types::zx_status_t {
86        match res {
87            Ok(()) => zx_types::ZX_OK,
88            Err(status) => status.0.get(),
89        }
90    }
91
92    /// Returns `Some(status)` if `raw` is not `ZX_OK`, otherwise returns `None`.
93    #[inline]
94    pub const fn try_from_raw(raw: zx_types::zx_status_t) -> Option<Self> {
95        match NonZero::new(raw) {
96            Some(err) => Some(Self(err)),
97            None => None,
98        }
99    }
100
101    /// Returns a `Status` for `raw`. If `raw` is `ZX_OK` (`0`), returns `Status::INTERNAL`.
102    #[inline]
103    pub const fn err_from_raw(raw: zx_types::zx_status_t) -> Self {
104        match Self::try_from_raw(raw) {
105            Some(status) => status,
106            None => Self::INTERNAL,
107        }
108    }
109
110    #[inline]
111    pub const fn into_raw(self) -> zx_types::zx_status_t {
112        self.0.get()
113    }
114}
115
116/// Convenience re-export of `Status::ok`.
117pub fn ok(raw: zx_types::zx_status_t) -> Result<(), Status> {
118    Status::ok(raw)
119}
120
121// LINT.IfChange(zx_status_t)
122assoc_values!(Status, [
123    #[doc = "The system encountered an otherwise unspecified error while performing the"]
124    #[doc = "operation."]
125    INTERNAL               = zx_types::ZX_ERR_INTERNAL;
126    #[doc = "The operation is not implemented, supported, or enabled."]
127    NOT_SUPPORTED          = zx_types::ZX_ERR_NOT_SUPPORTED;
128    #[doc = "The system was not able to allocate some resource needed for the operation."]
129    NO_RESOURCES           = zx_types::ZX_ERR_NO_RESOURCES;
130    #[doc = "The system was not able to allocate memory needed for the operation."]
131    NO_MEMORY              = zx_types::ZX_ERR_NO_MEMORY;
132    #[doc = "The system call was interrupted, but should be retried. This should not be"]
133    #[doc = "seen outside of the VDSO."]
134    INTERRUPTED_RETRY      = zx_types::ZX_ERR_INTERRUPTED_RETRY;
135    #[doc = "An argument is invalid. For example, a null pointer when a null pointer is"]
136    #[doc = "not permitted."]
137    INVALID_ARGS           = zx_types::ZX_ERR_INVALID_ARGS;
138    #[doc = "A specified handle value does not refer to a handle."]
139    BAD_HANDLE             = zx_types::ZX_ERR_BAD_HANDLE;
140    #[doc = "The subject of the operation is the wrong type to perform the operation."]
141    #[doc = ""]
142    #[doc = "For example: Attempting a message_read on a thread handle."]
143    WRONG_TYPE             = zx_types::ZX_ERR_WRONG_TYPE;
144    #[doc = "The specified syscall number is invalid."]
145    BAD_SYSCALL            = zx_types::ZX_ERR_BAD_SYSCALL;
146    #[doc = "An argument is outside the valid range for this operation."]
147    OUT_OF_RANGE           = zx_types::ZX_ERR_OUT_OF_RANGE;
148    #[doc = "The caller-provided buffer is too small for this operation."]
149    BUFFER_TOO_SMALL       = zx_types::ZX_ERR_BUFFER_TOO_SMALL;
150    #[doc = "The operation failed because the current state of the object does not allow"]
151    #[doc = "it, or a precondition of the operation is not satisfied."]
152    BAD_STATE              = zx_types::ZX_ERR_BAD_STATE;
153    #[doc = "The time limit for the operation elapsed before the operation completed."]
154    TIMED_OUT              = zx_types::ZX_ERR_TIMED_OUT;
155    #[doc = "The operation cannot be performed currently but potentially could succeed if"]
156    #[doc = "the caller waits for a prerequisite to be satisfied, like waiting for a"]
157    #[doc = "handle to be readable or writable."]
158    #[doc = ""]
159    #[doc = "Example: Attempting to read from a channel that has no messages waiting but"]
160    #[doc = "has an open remote will return `ZX_ERR_SHOULD_WAIT`. In contrast, attempting"]
161    #[doc = "to read from a channel that has no messages waiting and has a closed remote"]
162    #[doc = "end will return `ZX_ERR_PEER_CLOSED`."]
163    SHOULD_WAIT            = zx_types::ZX_ERR_SHOULD_WAIT;
164    #[doc = "The in-progress operation, for example, a wait, has been canceled."]
165    CANCELED               = zx_types::ZX_ERR_CANCELED;
166    #[doc = "The operation failed because the remote end of the subject of the operation"]
167    #[doc = "was closed."]
168    PEER_CLOSED            = zx_types::ZX_ERR_PEER_CLOSED;
169    #[doc = "The requested entity is not found."]
170    NOT_FOUND              = zx_types::ZX_ERR_NOT_FOUND;
171    #[doc = "An object with the specified identifier already exists."]
172    #[doc = ""]
173    #[doc = "Example: Attempting to create a file when a file already exists with that"]
174    #[doc = "name."]
175    ALREADY_EXISTS         = zx_types::ZX_ERR_ALREADY_EXISTS;
176    #[doc = "The operation failed because the named entity is already owned or controlled"]
177    #[doc = "by another entity. The operation could succeed later if the current owner"]
178    #[doc = "releases the entity."]
179    ALREADY_BOUND          = zx_types::ZX_ERR_ALREADY_BOUND;
180    #[doc = "The subject of the operation is currently unable to perform the operation."]
181    #[doc = ""]
182    #[doc = "This is used when there's no direct way for the caller to observe when the"]
183    #[doc = "subject will be able to perform the operation and should thus retry."]
184    UNAVAILABLE            = zx_types::ZX_ERR_UNAVAILABLE;
185    #[doc = "The caller did not have permission to perform the specified operation."]
186    ACCESS_DENIED          = zx_types::ZX_ERR_ACCESS_DENIED;
187    #[doc = "Otherwise-unspecified error occurred during I/O."]
188    IO                     = zx_types::ZX_ERR_IO;
189    #[doc = "The entity the I/O operation is being performed on rejected the operation."]
190    #[doc = ""]
191    #[doc = "Example: an I2C device NAK'ing a transaction or a disk controller rejecting"]
192    #[doc = "an invalid command, or a stalled USB endpoint."]
193    IO_REFUSED             = zx_types::ZX_ERR_IO_REFUSED;
194    #[doc = "The data in the operation failed an integrity check and is possibly"]
195    #[doc = "corrupted."]
196    #[doc = ""]
197    #[doc = "Example: CRC or Parity error."]
198    IO_DATA_INTEGRITY      = zx_types::ZX_ERR_IO_DATA_INTEGRITY;
199    #[doc = "The data in the operation is currently unavailable and may be permanently"]
200    #[doc = "lost."]
201    #[doc = ""]
202    #[doc = "Example: A disk block is irrecoverably damaged."]
203    IO_DATA_LOSS           = zx_types::ZX_ERR_IO_DATA_LOSS;
204    #[doc = "The device is no longer available (has been unplugged from the system,"]
205    #[doc = "powered down, or the driver has been unloaded)."]
206    IO_NOT_PRESENT         = zx_types::ZX_ERR_IO_NOT_PRESENT;
207    #[doc = "More data was received from the device than expected."]
208    #[doc = ""]
209    #[doc = "Example: a USB \"babble\" error due to a device sending more data than the"]
210    #[doc = "host queued to receive."]
211    IO_OVERRUN             = zx_types::ZX_ERR_IO_OVERRUN;
212    #[doc = "An operation did not complete within the required timeframe."]
213    #[doc = ""]
214    #[doc = "Example: A USB isochronous transfer that failed to complete due to an"]
215    #[doc = "overrun or underrun."]
216    IO_MISSED_DEADLINE     = zx_types::ZX_ERR_IO_MISSED_DEADLINE;
217    #[doc = "The data in the operation is invalid parameter or is out of range."]
218    #[doc = ""]
219    #[doc = "Example: A USB transfer that failed to complete with TRB Error"]
220    IO_INVALID             = zx_types::ZX_ERR_IO_INVALID;
221    #[doc = "Path name is too long."]
222    BAD_PATH               = zx_types::ZX_ERR_BAD_PATH;
223    #[doc = "The object is not a directory or does not support directory operations."]
224    #[doc = ""]
225    #[doc = "Example: Attempted to open a file as a directory or attempted to do"]
226    #[doc = "directory operations on a file."]
227    NOT_DIR                = zx_types::ZX_ERR_NOT_DIR;
228    #[doc = "Object is not a regular file."]
229    NOT_FILE               = zx_types::ZX_ERR_NOT_FILE;
230    #[doc = "This operation would cause a file to exceed a filesystem-specific size"]
231    #[doc = "limit."]
232    FILE_BIG               = zx_types::ZX_ERR_FILE_BIG;
233    #[doc = "The filesystem or device space is exhausted."]
234    NO_SPACE               = zx_types::ZX_ERR_NO_SPACE;
235    #[doc = "The directory is not empty for an operation that requires it to be empty."]
236    #[doc = ""]
237    #[doc = "For example, non-recursively deleting a directory with files still in it."]
238    NOT_EMPTY              = zx_types::ZX_ERR_NOT_EMPTY;
239    #[doc = "An indicate to not call again."]
240    #[doc = ""]
241    #[doc = "The flow control values `ZX_ERR_STOP`, `ZX_ERR_NEXT`, and `ZX_ERR_ASYNC` are"]
242    #[doc = "not errors and will never be returned by a system call or public API. They"]
243    #[doc = "allow callbacks to request their caller perform some other operation."]
244    #[doc = ""]
245    #[doc = "For example, a callback might be called on every event until it returns"]
246    #[doc = "something other than `ZX_OK`. This status allows differentiation between"]
247    #[doc = "\"stop due to an error\" and \"stop because work is done.\""]
248    STOP                   = zx_types::ZX_ERR_STOP;
249    #[doc = "Advance to the next item."]
250    #[doc = ""]
251    #[doc = "The flow control values `ZX_ERR_STOP`, `ZX_ERR_NEXT`, and `ZX_ERR_ASYNC` are"]
252    #[doc = "not errors and will never be returned by a system call or public API. They"]
253    #[doc = "allow callbacks to request their caller perform some other operation."]
254    #[doc = ""]
255    #[doc = "For example, a callback could use this value to indicate it did not consume"]
256    #[doc = "an item passed to it, but by choice, not due to an error condition."]
257    NEXT                   = zx_types::ZX_ERR_NEXT;
258    #[doc = "Ownership of the item has moved to an asynchronous worker."]
259    #[doc = ""]
260    #[doc = "The flow control values `ZX_ERR_STOP`, `ZX_ERR_NEXT`, and `ZX_ERR_ASYNC` are"]
261    #[doc = "not errors and will never be returned by a system call or public API. They"]
262    #[doc = "allow callbacks to request their caller perform some other operation."]
263    #[doc = ""]
264    #[doc = "Unlike `ZX_ERR_STOP`, which implies that iteration on an object"]
265    #[doc = "should stop, and `ZX_ERR_NEXT`, which implies that iteration"]
266    #[doc = "should continue to the next item, `ZX_ERR_ASYNC` implies"]
267    #[doc = "that an asynchronous worker is responsible for continuing iteration."]
268    #[doc = ""]
269    #[doc = "For example, a callback will be called on every event, but one event needs"]
270    #[doc = "to handle some work asynchronously before it can continue. `ZX_ERR_ASYNC`"]
271    #[doc = "implies the worker is responsible for resuming iteration once its work has"]
272    #[doc = "completed."]
273    ASYNC                  = zx_types::ZX_ERR_ASYNC;
274    #[doc = "The specified protocol is not supported."]
275    PROTOCOL_NOT_SUPPORTED = zx_types::ZX_ERR_PROTOCOL_NOT_SUPPORTED;
276    #[doc = "The host is unreachable."]
277    ADDRESS_UNREACHABLE    = zx_types::ZX_ERR_ADDRESS_UNREACHABLE;
278    #[doc = "Address is being used by someone else."]
279    ADDRESS_IN_USE         = zx_types::ZX_ERR_ADDRESS_IN_USE;
280    #[doc = "The socket is not connected."]
281    NOT_CONNECTED          = zx_types::ZX_ERR_NOT_CONNECTED;
282    #[doc = "The remote peer rejected the connection."]
283    CONNECTION_REFUSED     = zx_types::ZX_ERR_CONNECTION_REFUSED;
284    #[doc = "The connection was reset."]
285    CONNECTION_RESET       = zx_types::ZX_ERR_CONNECTION_RESET;
286    #[doc = "The connection was aborted."]
287    CONNECTION_ABORTED     = zx_types::ZX_ERR_CONNECTION_ABORTED;
288]);
289// LINT.ThenChange(//zircon/vdso/errors.fidl)
290
291impl fmt::Display for Status {
292    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
293        match self.assoc_const_name() {
294            Some(name) => name.fmt(f),
295            None => write!(f, "Unknown zircon status code: {}", self.0.get()),
296        }
297    }
298}
299
300impl core::error::Error for Status {}
301
302impl From<NonZero<zx_types::zx_status_t>> for Status {
303    #[inline]
304    fn from(val: NonZero<zx_types::zx_status_t>) -> Self {
305        Status(val)
306    }
307}
308
309impl From<Status> for NonZero<zx_types::zx_status_t> {
310    #[inline]
311    fn from(status: Status) -> Self {
312        status.0
313    }
314}
315
316impl From<Status> for zx_types::zx_status_t {
317    #[inline]
318    fn from(status: Status) -> zx_types::zx_status_t {
319        status.into_raw()
320    }
321}
322
323impl TryFrom<zx_types::zx_status_t> for Status {
324    type Error = TryFromRawError;
325
326    #[inline]
327    fn try_from(raw: zx_types::zx_status_t) -> Result<Self, Self::Error> {
328        Status::try_from_raw(raw).ok_or(TryFromRawError)
329    }
330}
331
332#[derive(Copy, Clone, Debug, Eq, PartialEq)]
333pub struct TryFromRawError;
334
335impl fmt::Display for TryFromRawError {
336    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
337        write!(f, "cannot convert ZX_OK (0) to Status")
338    }
339}
340
341impl core::error::Error for TryFromRawError {}
342
343impl From<Status> for Result<(), Status> {
344    #[inline]
345    fn from(src: Status) -> Result<(), Status> {
346        Err(src)
347    }
348}
349
350impl From<core::convert::Infallible> for Status {
351    fn from(x: core::convert::Infallible) -> Status {
352        match x {}
353    }
354}
355
356#[cfg(test)]
357mod test {
358    extern crate std;
359    use super::Status;
360
361    #[test]
362    fn status_debug_format() {
363        let cases = [
364            ("Status(BAD_SYSCALL)", Status::BAD_SYSCALL),
365            ("Status(NEXT)", Status::NEXT),
366            ("Status(-5050)", Status::err_from_raw(-5050)),
367        ];
368        for &(expected, value) in &cases {
369            assert_eq!(expected, std::format!("{value:?}"));
370        }
371    }
372
373    #[test]
374    fn status_into_result() {
375        let err_result: Result<(), Status> = Status::BAD_SYSCALL.into();
376        assert_eq!(err_result, Err(Status::BAD_SYSCALL));
377    }
378
379    #[test]
380    fn status_layout() {
381        use core::mem::size_of;
382        assert_eq!(size_of::<Status>(), 4);
383        assert_eq!(size_of::<Result<(), Status>>(), 4);
384    }
385
386    #[test]
387    fn test_err_from_raw() {
388        assert_eq!(Status::err_from_raw(zx_types::ZX_OK), Status::INTERNAL);
389        assert_eq!(Status::err_from_raw(zx_types::ZX_ERR_NOT_FOUND), Status::NOT_FOUND);
390    }
391
392    #[test]
393    fn test_try_from() {
394        use super::TryFromRawError;
395        assert_eq!(Status::try_from(zx_types::ZX_OK), Err(TryFromRawError));
396        assert_eq!(Status::try_from(zx_types::ZX_ERR_NOT_FOUND), Ok(Status::NOT_FOUND));
397    }
398
399    #[test]
400    fn test_as_str() {
401        assert_eq!(Status::INVALID_ARGS.as_str(), "INVALID_ARGS");
402        assert_eq!(Status::err_from_raw(-9999).as_str(), "UNKNOWN");
403    }
404}