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}