Skip to main content

pw_status/
pw_status.rs

1// Copyright 2023 The Pigweed Authors
2//
3// Licensed under the Apache License, Version 2.0 (the "License"); you may not
4// use this file except in compliance with the License. You may obtain a copy of
5// the License at
6//
7//     https://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12// License for the specific language governing permissions and limitations under
13// the License.
14
15//! # pw_status
16//!
17//! Rust error types using error codes compatible with Pigweed's
18//! [pw_status](https://pigweed.dev/pw_status).  In order to keep the interface
19//! idiomatic for Rust, `PW_STATUS_OK` is omitted from the Error enum and a
20//! `StatusCode` trait is provided to turn a `Result` into a canonical
21//! status code.
22//!
23//! For an in depth explanation of the values of the `Error` enum, see
24//! the [Pigweed status codes documentation](https://pigweed.dev/pw_status/#status-codes).
25//!
26//! # Example
27//!
28//! ```
29//! use pw_status::{Error, Result};
30//!
31//! fn div(numerator: u32, denominator: u32) -> Result<u32> {
32//!     if denominator == 0 {
33//!         Err(Error::FailedPrecondition)
34//!     } else {
35//!         Ok(numerator / denominator)
36//!     }
37//! }
38//!
39//! assert_eq!(div(4, 2), Ok(2));
40//! assert_eq!(div(4, 0), Err(Error::FailedPrecondition));
41//! ```
42#![no_std]
43
44/// Status code for no error.
45pub const OK: u32 = 0;
46
47/// Error type compatible with Pigweed's [pw_status](https://pigweed.dev/pw_status).
48///
49/// For an in depth explanation of the values of the `Error` enum, see
50/// the [Pigweed status codes documentation](https://pigweed.dev/pw_status/#status-codes).
51#[derive(Clone, Copy, Debug, Eq, PartialEq)]
52#[repr(u32)]
53pub enum Error {
54    Cancelled = 1,
55    Unknown = 2,
56    InvalidArgument = 3,
57    DeadlineExceeded = 4,
58    NotFound = 5,
59    AlreadyExists = 6,
60    PermissionDenied = 7,
61    ResourceExhausted = 8,
62    FailedPrecondition = 9,
63    Aborted = 10,
64    OutOfRange = 11,
65    Unimplemented = 12,
66    Internal = 13,
67    Unavailable = 14,
68    DataLoss = 15,
69    Unauthenticated = 16,
70}
71
72impl TryFrom<u32> for Error {
73    type Error = Self;
74
75    fn try_from(val: u32) -> core::result::Result<Self, Self::Error> {
76        match val {
77            1 => Ok(Error::Cancelled),
78            2 => Ok(Error::Unknown),
79            3 => Ok(Error::InvalidArgument),
80            4 => Ok(Error::DeadlineExceeded),
81            5 => Ok(Error::NotFound),
82            6 => Ok(Error::AlreadyExists),
83            7 => Ok(Error::PermissionDenied),
84            8 => Ok(Error::ResourceExhausted),
85            9 => Ok(Error::FailedPrecondition),
86            10 => Ok(Error::Aborted),
87            11 => Ok(Error::OutOfRange),
88            12 => Ok(Error::Unimplemented),
89            13 => Ok(Error::Internal),
90            14 => Ok(Error::Unavailable),
91            15 => Ok(Error::DataLoss),
92            16 => Ok(Error::Unauthenticated),
93            _ => Err(Error::InvalidArgument),
94        }
95    }
96}
97
98pub type Result<T> = core::result::Result<T, Error>;
99
100/// Convert a Result into an status code.
101pub trait StatusCode {
102    /// Return a pigweed compatible status code.
103    fn status_code(self) -> u32;
104}
105
106impl<T> StatusCode for Result<T> {
107    fn status_code(self) -> u32 {
108        match self {
109            Ok(_) => OK,
110            Err(e) => e as u32,
111        }
112    }
113}
114
115/// Convert a raw Pigweed status code into a Result.
116///
117/// # Returns
118/// If `status` is `OK` (0): `Ok(())`.
119/// If `status` is a valid [`Error`] code: `Err(Error)`.
120/// Otherwise: `Err(Error::Unknown)`.
121pub fn status_to_result(status: u32) -> Result<()> {
122    if status == OK {
123        Ok(())
124    } else {
125        match Error::try_from(status) {
126            Ok(e) => Err(e),
127            Err(_) => Err(Error::Unknown),
128        }
129    }
130}
131
132#[cfg(test)]
133mod tests {
134    use super::*;
135    #[test]
136    fn test_status_code() {
137        assert_eq!(Result::Ok(()).status_code(), 0);
138        assert_eq!(Result::<()>::Err(Error::Cancelled).status_code(), 1);
139        assert_eq!(Result::<()>::Err(Error::Unknown).status_code(), 2);
140        assert_eq!(Result::<()>::Err(Error::InvalidArgument).status_code(), 3);
141        assert_eq!(Result::<()>::Err(Error::DeadlineExceeded).status_code(), 4);
142        assert_eq!(Result::<()>::Err(Error::NotFound).status_code(), 5);
143        assert_eq!(Result::<()>::Err(Error::AlreadyExists).status_code(), 6);
144        assert_eq!(Result::<()>::Err(Error::PermissionDenied).status_code(), 7);
145        assert_eq!(Result::<()>::Err(Error::ResourceExhausted).status_code(), 8);
146        assert_eq!(
147            Result::<()>::Err(Error::FailedPrecondition).status_code(),
148            9
149        );
150        assert_eq!(Result::<()>::Err(Error::Aborted).status_code(), 10);
151        assert_eq!(Result::<()>::Err(Error::OutOfRange).status_code(), 11);
152        assert_eq!(Result::<()>::Err(Error::Unimplemented).status_code(), 12);
153        assert_eq!(Result::<()>::Err(Error::Internal).status_code(), 13);
154        assert_eq!(Result::<()>::Err(Error::Unavailable).status_code(), 14);
155        assert_eq!(Result::<()>::Err(Error::DataLoss).status_code(), 15);
156        assert_eq!(Result::<()>::Err(Error::Unauthenticated).status_code(), 16);
157    }
158
159    #[test]
160    fn test_status_to_result() {
161        assert_eq!(status_to_result(0), Ok(()));
162        assert_eq!(status_to_result(1), Err(Error::Cancelled));
163        assert_eq!(status_to_result(13), Err(Error::Internal));
164        assert_eq!(status_to_result(999), Err(Error::Unknown));
165    }
166}