1 //! Facilities for dealing with UEFI operation results.
2 
3 use core::fmt::Debug;
4 
5 /// The error type that we use, essentially a status code + optional additional data
6 mod error;
7 pub use error::Error;
8 
9 /// Definition of UEFI's standard status codes
10 mod status;
11 pub use status::{Status, StatusExt};
12 
13 /// Return type of most UEFI functions. Both success and error payloads are optional.
14 ///
15 /// Almost all UEFI operations provide a status code as an output which
16 /// indicates either success, a warning, or an error. This type alias maps
17 /// [`Status::SUCCESS`] to the `Ok` variant (with optional `Output` data), and
18 /// maps both warning and error statuses to the `Err` variant of type [`Error`],
19 /// which may carry optional inner `ErrData`.
20 ///
21 /// Warnings are treated as errors by default because they generally indicate
22 /// an abnormal situation.
23 ///
24 /// Some convenience methods are provided by the [`ResultExt`] trait.
25 pub type Result<Output = (), ErrData = ()> = core::result::Result<Output, Error<ErrData>>;
26 
27 /// Extension trait which provides some convenience methods for [`Result`].
28 pub trait ResultExt<Output, ErrData: Debug> {
29     /// Extract the UEFI status from this result
status(&self) -> Status30     fn status(&self) -> Status;
31 
32     /// Transform the ErrData value to ()
discard_errdata(self) -> Result<Output>33     fn discard_errdata(self) -> Result<Output>;
34 
35     /// Calls `op` if the result contains a warning, otherwise returns
36     /// the result unchanged.
37     ///
38     /// By default warning statuses are treated as errors (i.e. stored in the
39     /// `Err` variant) because they generally indicate an abnormal
40     /// situation. In rare cases though it may be helpful to handle a
41     /// warning. This method is similar to [`Result::or_else`], except that
42     /// `op` is called only when the status is a warning.
43     ///
44     /// # Example
45     ///
46     /// ```
47     /// use uefi::{Result, ResultExt, Status};
48     ///
49     /// # use uefi::StatusExt;
50     /// # fn x() -> uefi::Result {
51     /// # let some_result = Status::WARN_RESET_REQUIRED.to_result();
52     /// // Treat a specific warning as success, propagate others as errors.
53     /// some_result.handle_warning(|err| {
54     ///     if err.status() == Status::WARN_RESET_REQUIRED {
55     ///         Ok(())
56     ///     } else {
57     ///         Err(err)
58     ///     }
59     /// })?;
60     /// # Status::SUCCESS.to_result()
61     /// # }
62     /// ```
handle_warning<O>(self, op: O) -> Result<Output, ErrData> where O: FnOnce(Error<ErrData>) -> Result<Output, ErrData>63     fn handle_warning<O>(self, op: O) -> Result<Output, ErrData>
64     where
65         O: FnOnce(Error<ErrData>) -> Result<Output, ErrData>;
66 }
67 
68 impl<Output, ErrData: Debug> ResultExt<Output, ErrData> for Result<Output, ErrData> {
status(&self) -> Status69     fn status(&self) -> Status {
70         match self {
71             Ok(_) => Status::SUCCESS,
72             Err(e) => e.status(),
73         }
74     }
75 
discard_errdata(self) -> Result<Output>76     fn discard_errdata(self) -> Result<Output> {
77         match self {
78             Ok(o) => Ok(o),
79             Err(e) => Err(e.status().into()),
80         }
81     }
82 
handle_warning<O>(self, op: O) -> Self where O: FnOnce(Error<ErrData>) -> Self,83     fn handle_warning<O>(self, op: O) -> Self
84     where
85         O: FnOnce(Error<ErrData>) -> Self,
86     {
87         match self {
88             Ok(output) => Ok(output),
89             Err(err) => {
90                 if err.status().is_warning() {
91                     op(err)
92                 } else {
93                     Err(err)
94                 }
95             }
96         }
97     }
98 }
99