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
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
use unreachable::unreachable;
use io::WriteExt;
use std::process;
use std::error::Error;
use std::io::{self, Write};
/// Extension for Option-like types
pub trait OptionalExt {
/// The "success" variant of this optional type.
type Succ;
/// Unwrap or abort program with exit code
fn try(self, stderr: &mut io::Stderr) -> Self::Succ;
/// Unwrap or abort the program with failed exit code and custom error message
fn fail<'a>(self, err: &'a str, stderr: &mut io::Stderr) -> Self::Succ;
/// Consume an optional type, and write a warning to stderr if it is the "fail" value. If it is
/// the "success" value, return that. If not, return None.
fn warn(self, stderr: &mut io::Stderr) -> Option<Self::Succ>;
/// An unwrapping where the fail-case is not checked and threaten as statical unreachable.
unsafe fn unchecked_unwrap(self) -> Self::Succ;
}
impl<T, U: Error> OptionalExt for Result<T, U> {
type Succ = T;
fn try(self, stderr: &mut io::Stderr) -> T {
self.unwrap_or_else(|e| {
let mut stderr = stderr.lock();
// We ignore the results to avoid stack overflow (because of unbounded
// recursion).
let _ = stderr.write(b"error: ");
let _ = stderr.write(e.description().as_bytes());
let _ = stderr.write(b"\n");
let _ = stderr.flush();
process::exit(1);
})
}
fn fail<'a>(self, err: &'a str, stderr: &mut io::Stderr) -> T {
self.unwrap_or_else(|_| {
let mut stderr = stderr.lock();
let _ = stderr.write(b"error: ");
let _ = stderr.write(err.as_bytes());
let _ = stderr.write(b"\n");
let _ = stderr.flush();
process::exit(1);
})
}
fn warn(self, stderr: &mut io::Stderr) -> Option<T> {
if let Err(ref e) = self {
let mut stderr = stderr.lock();
let _ = stderr.write(b"warning: ");
let _ = stderr.write(e.description().as_bytes());
let _ = stderr.write(b"\n");
let _ = stderr.flush();
}
self.ok()
}
unsafe fn unchecked_unwrap(self) -> T {
if let Ok(x) = self {
x
} else {
unreachable()
}
}
}
impl<T> OptionalExt for Option<T> {
type Succ = T;
fn try(self, stderr: &mut io::Stderr) -> T {
self.unwrap_or_else(|| {
let mut stderr = stderr.lock();
let _ = stderr.writeln(b"error: (no message)\n");
let _ = stderr.flush();
process::exit(1);
})
}
fn fail<'a>(self, err: &'a str, stderr: &mut io::Stderr) -> T {
self.unwrap_or_else(|| {
let mut stderr = stderr.lock();
let _ = stderr.write(b"error:");
let _ = stderr.write(err.as_bytes());
let _ = stderr.write(b"\n");
let _ = stderr.flush();
process::exit(1);
})
}
fn warn(self, stderr: &mut io::Stderr) -> Option<T> {
if self.is_none() {
let mut stderr = stderr.lock();
let _ = stderr.writeln(b"warning: (no message)\n");
let _ = stderr.flush();
}
self
}
unsafe fn unchecked_unwrap(self) -> T {
if let Some(x) = self {
x
} else {
unreachable()
}
}
}
/// Extension for `Option<T>`.
pub trait OptionExt: OptionalExt {
/// Filter this Option.
///
/// This takes a closure which returns a boolean. If true, nothing will change. If false, it
/// will set the option to `None`.
fn filter<F>(self, filter: F) -> Self where F: FnOnce(&Self::Succ) -> bool;
}
impl<T> OptionExt for Option<T> {
fn filter<F>(self, filter: F) -> Self where F: FnOnce(&T) -> bool {
match self {
Some(ref x) if !filter(x) => None,
_ => self,
}
}
}
/// A generalization of `try!()`.
///
/// If this optional (`Option`-like) type is successful, return the inner value. If not, evaluate
/// the expression right to the arrow.
///
/// ## How is this different than `unwrap_or()`?
///
/// Unwrap or evaluates inside an closure, thus it cannot access various statement related to
/// control flow. For example, `return` will return the value from the closure, whereas `maybe!`,
/// will expand the statement inline, such that the current function will return.
#[macro_export]
macro_rules! maybe {
($x:expr) => {
if let Some(x) = $x {
x
} else {
return None;
}
};
($a:expr => $b:expr) => {
if let Some(x) = $a.into_iter().next() {
x
} else {
$b
}
};
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_maybe() {
fn func() -> Option<u8> {
loop {
maybe!(None => break);
}
maybe!(None);
unreachable!();
}
assert!(func().is_none());
}
#[test]
fn test_filter() {
assert_eq!(Some(3).filter(|x| x & 1 == 0), None);
assert_eq!(Some(2).filter(|x| x & 1 == 0), Some(2));
}
}