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