Skip to content

Commit e32ff66

Browse files
committed
Add more ergonomic ways to deal with uefi-rs' Results
1 parent d0f4722 commit e32ff66

File tree

4 files changed

+130
-89
lines changed

4 files changed

+130
-89
lines changed

src/error/completion.rs

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
use super::Status;
2+
use log::warn;
3+
4+
/// This type is used when an UEFI operation has completed, but some non-fatal
5+
/// problems may have been encountered along the way
6+
#[must_use]
7+
#[derive(Clone, Copy, Debug, PartialEq)]
8+
pub enum Completion<T> {
9+
/// The operation completed without problems
10+
Success(T),
11+
12+
/// The operation completed, but some non-fatal issues were encountered
13+
Warning(T, Status),
14+
}
15+
16+
impl<T> Completion<T> {
17+
/// Split the completion into a (status, value) pair
18+
pub fn split(self) -> (T, Status) {
19+
match self {
20+
Completion::Success(res) => (res, Status::SUCCESS),
21+
Completion::Warning(res, stat) => (res, stat),
22+
}
23+
}
24+
25+
/// Access the inner value, logging the warning if there is any
26+
pub fn value(self) -> T {
27+
match self {
28+
Completion::Success(res) => res,
29+
Completion::Warning(res, stat) => {
30+
warn!("Encountered UEFI warning: {:?}", stat);
31+
res
32+
}
33+
}
34+
}
35+
36+
/// Assume that no warning occured, panic if not
37+
pub fn unwrap(self) -> T {
38+
match self {
39+
Completion::Success(res) => res,
40+
Completion::Warning(_, w) => {
41+
unwrap_failed("Called `Completion::unwrap()` on a `Warning` value", w)
42+
}
43+
}
44+
}
45+
46+
/// Assume that no warning occured, panic with provided message if not
47+
pub fn expect(self, msg: &str) -> T {
48+
match self {
49+
Completion::Success(res) => res,
50+
Completion::Warning(_, w) => unwrap_failed(msg, w),
51+
}
52+
}
53+
54+
/// Transform the inner value without unwrapping the Completion
55+
pub fn map<U>(self, f: impl Fn(T) -> U) -> Completion<U> {
56+
match self {
57+
Completion::Success(res) => Completion::Success(f(res)),
58+
Completion::Warning(res, stat) => Completion::Warning(f(res), stat),
59+
}
60+
}
61+
62+
/// Merge this completion with a success or warning status
63+
///
64+
/// Since this type only has storage for one warning, if two warnings must
65+
/// be stored, one of them will be spilled into the logs.
66+
///
67+
pub fn with_warning(self, extra_stat: Status) -> Self {
68+
match self {
69+
Completion::Success(res) => {
70+
if extra_stat.is_success() {
71+
Completion::Success(res)
72+
} else {
73+
Completion::Warning(res, extra_stat)
74+
}
75+
}
76+
Completion::Warning(res, stat) => {
77+
if extra_stat.is_success() {
78+
Completion::Warning(res, stat)
79+
} else {
80+
warn!("Encountered UEFI warning {:?}", stat);
81+
Completion::Warning(res, extra_stat)
82+
}
83+
}
84+
}
85+
}
86+
}
87+
88+
impl<T> From<T> for Completion<T> {
89+
fn from(res: T) -> Self {
90+
Completion::Success(res)
91+
}
92+
}
93+
94+
// This is a separate function to reduce the code size of the methods
95+
#[inline(never)]
96+
#[cold]
97+
fn unwrap_failed(msg: &str, warning: Status) -> ! {
98+
panic!("{}: {:?}", msg, warning)
99+
}

src/error/mod.rs

Lines changed: 29 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -1,106 +1,48 @@
1-
use core::result;
2-
use log::warn;
3-
41
/// Definition of UEFI's standard status codes
52
mod status;
63
pub use self::status::Status;
74

8-
/// This type is used when an UEFI operation has completed, but some non-fatal
9-
/// problems may have been encountered along the way
10-
#[must_use]
11-
#[derive(Clone, Copy, Debug, PartialEq)]
12-
pub enum Completion<T> {
13-
/// The operation completed without problems
14-
Success(T),
5+
/// Completions are used to model operations which have completed, but may have
6+
/// encountered non-fatal errors ("warnings") along the way
7+
mod completion;
8+
pub use self::completion::Completion;
159

16-
/// The operation completed, but some non-fatal issues were encountered
17-
Warning(T, Status),
18-
}
10+
/// Return type of many UEFI functions.
11+
pub type Result<T> = core::result::Result<Completion<T>, Status>;
1912

20-
impl<T> Completion<T> {
21-
/// Split the completion into a (status, value) pair
22-
pub fn split(self) -> (T, Status) {
23-
match self {
24-
Completion::Success(res) => (res, Status::SUCCESS),
25-
Completion::Warning(res, stat) => (res, stat),
26-
}
27-
}
13+
/// Extension trait for Result which helps dealing with UEFI's warnings
14+
pub trait ResultExt<T> {
15+
/// Treat warnings as errors
16+
fn warn_error(self) -> core::result::Result<T, Status>;
2817

29-
/// Access the inner value, logging the warning if there is any
30-
pub fn value(self) -> T {
31-
match self {
32-
Completion::Success(res) => res,
33-
Completion::Warning(res, stat) => {
34-
warn!("Encountered UEFI warning: {:?}", stat);
35-
res
36-
}
37-
}
38-
}
18+
/// Ignore warnings, keepint a trace of them in the logs
19+
fn warn_log(self) -> core::result::Result<T, Status>;
3920

40-
/// Assume that no warning occured, panic if not
41-
pub fn unwrap(self) -> T {
42-
match self {
43-
Completion::Success(res) => res,
44-
Completion::Warning(_, w) => {
45-
unwrap_failed("Called `Completion::unwrap()` on a `Warning` value", w)
46-
}
47-
}
48-
}
21+
/// Expect success without warnings, panic otherwise
22+
fn warn_unwrap(self) -> T;
4923

50-
/// Assume that no warning occured, panic with provided message if not
51-
pub fn expect(self, msg: &str) -> T {
52-
match self {
53-
Completion::Success(res) => res,
54-
Completion::Warning(_, w) => unwrap_failed(msg, w),
55-
}
56-
}
24+
/// Expect success without warnings, panic with provided message otherwise
25+
fn warn_expect(self, msg: &str) -> T;
26+
}
5727

58-
/// Transform the inner value without unwrapping the Completion
59-
pub fn map<U>(self, f: impl Fn(T) -> U) -> Completion<U> {
28+
impl<T> ResultExt<T> for Result<T> {
29+
fn warn_error(self) -> core::result::Result<T, Status> {
6030
match self {
61-
Completion::Success(res) => Completion::Success(f(res)),
62-
Completion::Warning(res, stat) => Completion::Warning(f(res), stat),
31+
Ok(Completion::Success(v)) => Ok(v),
32+
Ok(Completion::Warning(_, s)) => Err(s),
33+
Err(s) => Err(s),
6334
}
6435
}
6536

66-
/// Merge this completion with a success or warning status
67-
///
68-
/// Since this type only has storage for one warning, if two warnings must
69-
/// be stored, one of them will be spilled into the logs.
70-
///
71-
pub fn with_warning(self, extra_stat: Status) -> Self {
72-
match self {
73-
Completion::Success(res) => {
74-
if extra_stat.is_success() {
75-
Completion::Success(res)
76-
} else {
77-
Completion::Warning(res, extra_stat)
78-
}
79-
}
80-
Completion::Warning(res, stat) => {
81-
if extra_stat.is_success() {
82-
Completion::Warning(res, stat)
83-
} else {
84-
warn!("Encountered UEFI warning {:?}", stat);
85-
Completion::Warning(res, extra_stat)
86-
}
87-
}
88-
}
37+
fn warn_log(self) -> core::result::Result<T, Status> {
38+
self.map(|completion| completion.value())
8939
}
90-
}
9140

92-
impl<T> From<T> for Completion<T> {
93-
fn from(res: T) -> Self {
94-
Completion::Success(res)
41+
fn warn_unwrap(self) -> T {
42+
self.unwrap().unwrap()
9543
}
96-
}
9744

98-
// This is a separate function to reduce the code size of the methods
99-
#[inline(never)]
100-
#[cold]
101-
fn unwrap_failed(msg: &str, warning: Status) -> ! {
102-
panic!("{}: {:?}", msg, warning)
45+
fn warn_expect(self, msg: &str) -> T {
46+
self.expect(msg).expect(msg)
47+
}
10348
}
104-
105-
/// Return type of many UEFI functions.
106-
pub type Result<T> = result::Result<Completion<T>, Status>;

src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ mod data_types;
3535
pub use self::data_types::{Event, Guid, Handle};
3636

3737
mod error;
38-
pub use self::error::{Completion, Result, Status};
38+
pub use self::error::{Completion, Result, ResultExt, Status};
3939

4040
pub mod table;
4141

src/prelude.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
//!
33
//! This includes the system table types, `Status` codes, etc.
44
5-
pub use crate::Status;
5+
pub use crate::{ResultExt, Status};
66

77
// Import the basic table types.
88
pub use crate::table::{boot::BootServices, runtime::RuntimeServices, SystemTable};

0 commit comments

Comments
 (0)