1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
//! Facilities for dealing with UEFI operation results.

use core::fmt::Debug;

/// The error type that we use, essentially a status code + optional additional data
mod error;
pub use self::error::Error;

/// Definition of UEFI's standard status codes
mod status;
pub use self::status::{Status, StatusExt};

/// Return type of most UEFI functions. Both success and error payloads are optional.
///
/// Almost all UEFI operations provide a status code as an output which
/// indicates either success, a warning, or an error. This type alias maps
/// [`Status::SUCCESS`] to the `Ok` variant (with optional `Output` data), and
/// maps both warning and error statuses to the `Err` variant of type [`Error`],
/// which may carry optional inner `ErrData`.
///
/// Warnings are treated as errors by default because they generally indicate
/// an abnormal situation.
///
/// Some convenience methods are provided by the [`ResultExt`] trait.
pub type Result<Output = (), ErrData = ()> = core::result::Result<Output, Error<ErrData>>;

/// Extension trait which provides some convenience methods for [`Result`].
pub trait ResultExt<Output, ErrData: Debug> {
    /// Extract the UEFI status from this result
    fn status(&self) -> Status;

    /// Transform the ErrData value to ()
    fn discard_errdata(self) -> Result<Output>;

    /// Calls `op` if the result contains a warning, otherwise returns
    /// the result unchanged.
    ///
    /// By default warning statuses are treated as errors (i.e. stored in the
    /// `Err` variant) because they generally indicate an abnormal
    /// situation. In rare cases though it may be helpful to handle a
    /// warning. This method is similar to [`Result::or_else`], except that
    /// `op` is called only when the status is a warning.
    ///
    /// # Example
    ///
    /// ```
    /// use uefi::{Result, ResultExt, Status};
    ///
    /// # use uefi::StatusExt;
    /// # fn x() -> uefi::Result {
    /// # let some_result = Status::WARN_RESET_REQUIRED.to_result();
    /// // Treat a specific warning as success, propagate others as errors.
    /// some_result.handle_warning(|err| {
    ///     if err.status() == Status::WARN_RESET_REQUIRED {
    ///         Ok(())
    ///     } else {
    ///         Err(err)
    ///     }
    /// })?;
    /// # Status::SUCCESS.to_result()
    /// # }
    /// ```
    fn handle_warning<O>(self, op: O) -> Result<Output, ErrData>
    where
        O: FnOnce(Error<ErrData>) -> Result<Output, ErrData>;
}

impl<Output, ErrData: Debug> ResultExt<Output, ErrData> for Result<Output, ErrData> {
    fn status(&self) -> Status {
        match self {
            Ok(_) => Status::SUCCESS,
            Err(e) => e.status(),
        }
    }

    fn discard_errdata(self) -> Result<Output> {
        match self {
            Ok(o) => Ok(o),
            Err(e) => Err(e.status().into()),
        }
    }

    fn handle_warning<O>(self, op: O) -> Result<Output, ErrData>
    where
        O: FnOnce(Error<ErrData>) -> Result<Output, ErrData>,
    {
        match self {
            Ok(output) => Ok(output),
            Err(err) => {
                if err.status().is_warning() {
                    op(err)
                } else {
                    Err(err)
                }
            }
        }
    }
}