blob: e73d597c8dc6b20c7b86ebb058c3838edbec4839 [file] [log] [blame]
// Copyright 2018 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//! Type-safe bindings for Zircon status.
use fuchsia_zircon_types as sys;
use std::error;
use std::ffi::NulError;
use std::fmt;
use std::io;
pub use sys::zx_status_t;
// Creates associated constants of TypeName of the form
// `pub const NAME: TypeName = TypeName(path::to::value);`
// and provides a private `assoc_const_name` method and a `Debug` implementation
// for the type based on `$name`.
// If multiple names match, the first will be used in `name` and `Debug`.
#[macro_export]
macro_rules! assoc_values {
($typename:ident, [$($(#[$attr:meta])* $name:ident = $value:path;)*]) => {
#[allow(non_upper_case_globals)]
impl $typename {
$(
$(#[$attr])*
pub const $name: $typename = $typename($value);
)*
fn assoc_const_name(&self) -> Option<&'static str> {
match self.0 {
$(
$(#[$attr])*
$value => Some(stringify!($name)),
)*
_ => None,
}
}
}
impl ::std::fmt::Debug for $typename {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
f.write_str(concat!(stringify!($typename), "("))?;
match self.assoc_const_name() {
Some(name) => f.write_str(&name)?,
None => ::std::fmt::Debug::fmt(&self.0, f)?,
}
f.write_str(")")
}
}
}
}
/// Status type indicating the result of a Fuchsia syscall.
///
/// This type is generally used to indicate the reason for an error.
/// While this type can contain `Status::OK` (`ZX_OK` in C land), elements of this type are
/// generally constructed using the `ok` method, which checks for `ZX_OK` and returns a
/// `Result<(), Status>` appropriately.
#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)]
#[repr(transparent)]
pub struct Status(sys::zx_status_t);
impl Status {
/// Returns `Ok(())` if the status was `OK`,
/// otherwise returns `Err(status)`.
pub fn ok(raw: sys::zx_status_t) -> Result<(), Status> {
if raw == Status::OK.0 {
Ok(())
} else {
Err(Status(raw))
}
}
pub fn from_raw(raw: sys::zx_status_t) -> Self {
Status(raw)
}
pub fn into_raw(self) -> sys::zx_status_t {
self.0
}
}
assoc_values!(Status, [
OK = sys::ZX_OK;
INTERNAL = sys::ZX_ERR_INTERNAL;
NOT_SUPPORTED = sys::ZX_ERR_NOT_SUPPORTED;
NO_RESOURCES = sys::ZX_ERR_NO_RESOURCES;
NO_MEMORY = sys::ZX_ERR_NO_MEMORY;
INTERRUPTED_RETRY = sys::ZX_ERR_INTERRUPTED_RETRY;
INVALID_ARGS = sys::ZX_ERR_INVALID_ARGS;
BAD_HANDLE = sys::ZX_ERR_BAD_HANDLE;
WRONG_TYPE = sys::ZX_ERR_WRONG_TYPE;
BAD_SYSCALL = sys::ZX_ERR_BAD_SYSCALL;
OUT_OF_RANGE = sys::ZX_ERR_OUT_OF_RANGE;
BUFFER_TOO_SMALL = sys::ZX_ERR_BUFFER_TOO_SMALL;
BAD_STATE = sys::ZX_ERR_BAD_STATE;
TIMED_OUT = sys::ZX_ERR_TIMED_OUT;
SHOULD_WAIT = sys::ZX_ERR_SHOULD_WAIT;
CANCELED = sys::ZX_ERR_CANCELED;
PEER_CLOSED = sys::ZX_ERR_PEER_CLOSED;
NOT_FOUND = sys::ZX_ERR_NOT_FOUND;
ALREADY_EXISTS = sys::ZX_ERR_ALREADY_EXISTS;
ALREADY_BOUND = sys::ZX_ERR_ALREADY_BOUND;
UNAVAILABLE = sys::ZX_ERR_UNAVAILABLE;
ACCESS_DENIED = sys::ZX_ERR_ACCESS_DENIED;
IO = sys::ZX_ERR_IO;
IO_REFUSED = sys::ZX_ERR_IO_REFUSED;
IO_DATA_INTEGRITY = sys::ZX_ERR_IO_DATA_INTEGRITY;
IO_DATA_LOSS = sys::ZX_ERR_IO_DATA_LOSS;
IO_NOT_PRESENT = sys::ZX_ERR_IO_NOT_PRESENT;
IO_OVERRUN = sys::ZX_ERR_IO_OVERRUN;
IO_MISSED_DEADLINE = sys::ZX_ERR_IO_MISSED_DEADLINE;
IO_INVALID = sys::ZX_ERR_IO_INVALID;
BAD_PATH = sys::ZX_ERR_BAD_PATH;
NOT_DIR = sys::ZX_ERR_NOT_DIR;
NOT_FILE = sys::ZX_ERR_NOT_FILE;
FILE_BIG = sys::ZX_ERR_FILE_BIG;
NO_SPACE = sys::ZX_ERR_NO_SPACE;
NOT_EMPTY = sys::ZX_ERR_NOT_EMPTY;
STOP = sys::ZX_ERR_STOP;
NEXT = sys::ZX_ERR_NEXT;
ASYNC = sys::ZX_ERR_ASYNC;
PROTOCOL_NOT_SUPPORTED = sys::ZX_ERR_PROTOCOL_NOT_SUPPORTED;
ADDRESS_UNREACHABLE = sys::ZX_ERR_ADDRESS_UNREACHABLE;
ADDRESS_IN_USE = sys::ZX_ERR_ADDRESS_IN_USE;
NOT_CONNECTED = sys::ZX_ERR_NOT_CONNECTED;
CONNECTION_REFUSED = sys::ZX_ERR_CONNECTION_REFUSED;
CONNECTION_RESET = sys::ZX_ERR_CONNECTION_RESET;
CONNECTION_ABORTED = sys::ZX_ERR_CONNECTION_ABORTED;
]);
impl Status {
pub fn into_io_error(self) -> io::Error {
self.into()
}
pub fn from_result(res: Result<(), Self>) -> Self {
res.into()
}
}
impl From<io::ErrorKind> for Status {
fn from(kind: io::ErrorKind) -> Self {
use std::io::ErrorKind::*;
match kind {
NotFound => Status::NOT_FOUND,
PermissionDenied => Status::ACCESS_DENIED,
ConnectionRefused => Status::IO_REFUSED,
ConnectionAborted => Status::PEER_CLOSED,
AddrInUse => Status::ALREADY_BOUND,
AddrNotAvailable => Status::UNAVAILABLE,
BrokenPipe => Status::PEER_CLOSED,
AlreadyExists => Status::ALREADY_EXISTS,
WouldBlock => Status::SHOULD_WAIT,
InvalidInput => Status::INVALID_ARGS,
TimedOut => Status::TIMED_OUT,
Interrupted => Status::INTERRUPTED_RETRY,
UnexpectedEof | WriteZero | ConnectionReset | NotConnected | Other | _ => Status::IO,
}
}
}
impl From<Status> for io::ErrorKind {
fn from(status: Status) -> io::ErrorKind {
use std::io::ErrorKind::*;
match status {
Status::INTERRUPTED_RETRY => Interrupted,
Status::BAD_HANDLE => BrokenPipe,
Status::TIMED_OUT => TimedOut,
Status::SHOULD_WAIT => WouldBlock,
Status::PEER_CLOSED => ConnectionAborted,
Status::NOT_FOUND => NotFound,
Status::ALREADY_EXISTS => AlreadyExists,
Status::ALREADY_BOUND => AlreadyExists,
Status::UNAVAILABLE => AddrNotAvailable,
Status::ACCESS_DENIED => PermissionDenied,
Status::IO_REFUSED => ConnectionRefused,
Status::IO_DATA_INTEGRITY => InvalidData,
Status::BAD_PATH | Status::INVALID_ARGS | Status::OUT_OF_RANGE | Status::WRONG_TYPE => {
InvalidInput
}
Status::OK
| Status::NEXT
| Status::STOP
| Status::NO_SPACE
| Status::FILE_BIG
| Status::NOT_FILE
| Status::NOT_DIR
| Status::IO_DATA_LOSS
| Status::IO
| Status::CANCELED
| Status::BAD_STATE
| Status::BUFFER_TOO_SMALL
| Status::BAD_SYSCALL
| Status::INTERNAL
| Status::NOT_SUPPORTED
| Status::NO_RESOURCES
| Status::NO_MEMORY
| _ => Other,
}
}
}
impl fmt::Display for Status {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self.assoc_const_name() {
Some(name) => name.fmt(f),
None => write!(f, "Unknown zircon status code: {}", self.0),
}
}
}
impl error::Error for Status {}
impl From<io::Error> for Status {
fn from(err: io::Error) -> Status {
err.kind().into()
}
}
impl From<Status> for io::Error {
fn from(status: Status) -> io::Error {
io::Error::from(io::ErrorKind::from(status))
}
}
impl From<NulError> for Status {
fn from(_error: NulError) -> Status {
Status::INVALID_ARGS
}
}
impl From<Result<(), Status>> for Status {
fn from(res: Result<(), Status>) -> Status {
match res {
Ok(()) => Self::OK,
Err(status) => status,
}
}
}
impl From<Status> for Result<(), Status> {
fn from(src: Status) -> Result<(), Status> {
Status::ok(src.into_raw())
}
}
#[cfg(test)]
mod test {
use super::Status;
#[test]
fn status_debug_format() {
let cases = [
("Status(OK)", Status::OK),
("Status(BAD_SYSCALL)", Status::BAD_SYSCALL),
("Status(NEXT)", Status::NEXT),
("Status(-5050)", Status(-5050)),
];
for &(expected, value) in &cases {
assert_eq!(expected, format!("{:?}", value));
}
}
#[test]
fn status_into_result() {
let ok_result: Result<(), Status> = Status::OK.into();
assert_eq!(ok_result, Ok(()));
let err_result: Result<(), Status> = Status::BAD_SYSCALL.into();
assert_eq!(err_result, Err(Status::BAD_SYSCALL));
}
}