fehler/lib.rs
1#![no_std]
2#[doc(inline)]
3/// Annotations a function that "throws" a Result.
4///
5/// Inside functions tagged with `throws`, you can use `?` and the `throw!` macro to return errors,
6/// but you don't need to wrap the successful return values in `Ok`.
7///
8/// Using this syntax, you can write fallible functions almost as if they were nonfallible. Every
9/// time a function call would return a `Result`, you "re-raise" the error using `?`, and if you
10/// wish to raise your own error, you can return it with the `throw!` macro.
11///
12/// ## Example
13/// ```should_panic
14/// use std::io::{self, Read};
15///
16/// use fehler::{throw, throws};
17///
18/// #[throws(io::Error)]
19/// fn main() {
20/// let mut file = std::fs::File::open("The_House_of_the_Spirits.txt")?;
21/// let mut text = String::new();
22/// file.read_to_string(&mut text)?;
23///
24/// if !text.starts_with("Barrabas came to us by sea, the child Clara wrote") {
25/// throw!(io::Error::from_raw_os_error(22));
26/// }
27///
28/// println!("Okay!");
29/// }
30/// ```
31///
32/// # Default Error Type
33///
34/// This macro supports a "default error type" - if you do not pass a type to the macro, it will
35/// use the type named `Error` in this scope. So if you have defined an error type in this
36/// module, that will be the error thrown by this function.
37///
38/// You can access this feature by omitting the arguments entirely or by passing `_` as the type.
39///
40/// ## Example
41///
42/// ```should_panic
43/// use fehler::throws;
44///
45/// // Set the default error type for this module:
46/// type Error = std::io::Error;
47///
48/// #[throws]
49/// fn main() {
50/// let file = std::fs::read_to_string("my_file.txt")?;
51/// println!("{}", file);
52/// }
53/// ```
54///
55/// # Throwing as an Option
56///
57/// This syntax can also support functions which return an `Option` instead of a `Result`. The
58/// way to access this is to pass `as Option` as the argument to `throw`.
59///
60/// In functions that return `Option`, you can use the `throw!()` macro without any argument to
61/// return `None`.
62///
63/// ## Example
64///
65/// ```
66/// use fehler::{throw, throws};
67///
68/// #[throws(as Option)]
69/// fn example<T: Eq + Ord>(slice: &[T], needle: &T) -> usize {
70/// if !slice.contains(needle) {
71/// throw!();
72/// }
73/// slice.binary_search(needle).ok()?
74/// }
75/// ```
76///
77/// # Other `Try` types
78///
79/// The `?` syntax in Rust is controlled by a trait called `Try`, which is currently unstable.
80/// Because this feature is unstable and I don't want to maintain compatibility if its interface
81/// changes, this crate currently only works with two stable `Try` types: Result and Option.
82/// However, its designed so that it will hopefully support other `Try` types as well in the
83/// future.
84///
85/// It's worth noting that `Try` also has some other stable implementations: specifically `Poll`.
86/// Because of the somewhat unusual implementation of `Try` for those types, this crate does not
87/// support `throws` syntax on functions that return `Poll` (so you can't use this syntax when
88/// implementing a Future by hand, for example). I hope to come up with a way to support Poll in
89/// the future.
90pub use fehler_macros::throws;
91
92/// Throw an error.
93///
94/// This macro is equivalent to `Err($err)?`.
95#[macro_export]
96macro_rules! throw {
97 ($err:expr) => (return <_ as $crate::__internal::_Throw>::from_error((::core::convert::From::from($err))));
98 () => (return <_ as ::core::default::Default>::default());
99}
100
101#[doc(hidden)]
102pub mod __internal {
103 pub trait _Succeed {
104 type Ok;
105 fn from_ok(ok: Self::Ok) -> Self;
106 }
107
108 pub trait _Throw {
109 type Error;
110 fn from_error(error: Self::Error) -> Self;
111 }
112
113 mod stable {
114 use core::task::Poll;
115
116 impl<T, E> super::_Succeed for Result<T, E> {
117 type Ok = T;
118 fn from_ok(ok: T) -> Self {
119 Ok(ok)
120 }
121 }
122
123 impl<T, E> super::_Throw for Result<T, E> {
124 type Error = E;
125 fn from_error(error: Self::Error) -> Self {
126 Err(error)
127 }
128 }
129
130 impl<T, E> super::_Succeed for Poll<Result<T, E>> {
131 type Ok = Poll<T>;
132
133 fn from_ok(ok: Self::Ok) -> Self {
134 match ok {
135 Poll::Ready(ok) => Poll::Ready(Ok(ok)),
136 Poll::Pending => Poll::Pending,
137 }
138 }
139 }
140
141 impl<T, E> super::_Throw for Poll<Result<T, E>> {
142 type Error = E;
143
144 fn from_error(error: Self::Error) -> Self {
145 Poll::Ready(Err(error))
146 }
147 }
148
149 impl<T, E> super::_Succeed for Poll<Option<Result<T, E>>> {
150 type Ok = Poll<Option<T>>;
151
152 fn from_ok(ok: Self::Ok) -> Self {
153 match ok {
154 Poll::Ready(Some(ok)) => Poll::Ready(Some(Ok(ok))),
155 Poll::Ready(None) => Poll::Ready(None),
156 Poll::Pending => Poll::Pending,
157 }
158 }
159 }
160
161 impl<T, E> super::_Throw for Poll<Option<Result<T, E>>> {
162 type Error = E;
163
164 fn from_error(error: Self::Error) -> Self {
165 Poll::Ready(Some(Err(error)))
166 }
167 }
168
169 impl<T> super::_Succeed for Option<T> {
170 type Ok = T;
171
172 fn from_ok(ok: Self::Ok) -> Self {
173 Some(ok)
174 }
175 }
176 }
177}