|
| 1 | +// https://doc.rust-lang.org/nightly/std/ops/trait.Try.html |
| 2 | +// https://github.com/rust-lang/rfcs/blob/master/text/3058-try-trait-v2.md |
| 3 | +// https://github.com/rust-lang/rust/issues/84277#issuecomment-905821708 |
| 4 | +// https://blog.rust-lang.org/2020/08/27/Rust-1.46.0.html#track_caller |
| 5 | + |
| 6 | +use fomat_macros::fomat; |
| 7 | +use std::fmt; |
| 8 | +use std::ops::{ControlFlow, FromResidual, Try}; |
| 9 | +use std::panic::Location; |
| 10 | +use super::filename; |
| 11 | + |
| 12 | +#[macro_export] |
| 13 | +macro_rules! fail {($($args: tt)+) => (return $crate::re::Re::fail (fomat! ($($args)+)))} |
| 14 | + |
| 15 | +#[derive(Debug)] |
| 16 | +#[must_use = "this `Re` may be an `Err` variant, which should be handled"] |
| 17 | +pub enum Re<T> {Ok (T), Err (String)} |
| 18 | + |
| 19 | +impl<T> Try for Re<T> { |
| 20 | + type Output = T; |
| 21 | + type Residual = Re<!>; |
| 22 | + |
| 23 | + #[inline] |
| 24 | + fn from_output (c: T) -> Self {Re::Ok (c)} |
| 25 | + |
| 26 | + #[inline] |
| 27 | + fn branch (self) -> ControlFlow<Self::Residual, T> { |
| 28 | + match self { |
| 29 | + Re::Ok (c) => ControlFlow::Continue (c), |
| 30 | + Re::Err (e) => ControlFlow::Break (Re::Err (e))}}} |
| 31 | + |
| 32 | +impl<T> FromResidual<Re<!>> for Re<T> { |
| 33 | + #[track_caller] |
| 34 | + fn from_residual (x: Re<!>) -> Self { |
| 35 | + let err = match x {Re::Ok (_) => unreachable!(), Re::Err (e) => e}; |
| 36 | + let loc = Location::caller(); |
| 37 | + let err = fomat! ((filename (loc.file())) ':' (loc.line()) "] " (err)); |
| 38 | + Re::Err (err)}} |
| 39 | + |
| 40 | +impl<T, O, E> FromResidual<Result<O, E>> for Re<T> where E: fmt::Display { |
| 41 | + #[track_caller] |
| 42 | + fn from_residual (x: Result<O, E>) -> Self { |
| 43 | + let err = match x {Result::Ok (_) => unreachable!(), Result::Err (e) => e}; |
| 44 | + let loc = Location::caller(); |
| 45 | + let err = fomat! ((filename (loc.file())) ':' (loc.line()) "] " (err)); |
| 46 | + Re::Err (err)}} |
| 47 | + |
| 48 | +impl<T> Re<T> { |
| 49 | + #[track_caller] |
| 50 | + pub fn fail<E: fmt::Display> (emsg: E) -> Re<T> { |
| 51 | + let loc = Location::caller(); |
| 52 | + let err = fomat! ((filename (loc.file())) ':' (loc.line()) "] " (emsg)); |
| 53 | + Re::Err (err)} |
| 54 | + |
| 55 | + #[inline] |
| 56 | + pub fn err (self) -> Option<String> { |
| 57 | + match self {Re::Ok (_) => None, Re::Err (e) => Some (e)}} |
| 58 | + |
| 59 | + #[inline] |
| 60 | + #[track_caller] |
| 61 | + pub fn expect (self, msg: &str) -> T { |
| 62 | + match self {Re::Ok (k) => k, Re::Err (err) => panic! ("{}: {:?}", msg, err)}} |
| 63 | + |
| 64 | + #[inline] |
| 65 | + #[track_caller] |
| 66 | + pub fn unwrap (self) -> T { |
| 67 | + match self {Re::Ok (k) => k, Re::Err (err) => panic! ("called `Re::unwrap()` on an `Err` value: {:?}", err)}} |
| 68 | + |
| 69 | + #[inline] |
| 70 | + pub fn unwrap_or (self, default: T) -> T { |
| 71 | + match self {Re::Ok (k) => k, Re::Err (_) => default}} |
| 72 | + |
| 73 | + #[inline] |
| 74 | + pub fn map<U, F: FnOnce (T) -> U> (self, op: F) -> Re<U> { |
| 75 | + match self {Re::Ok (k) => Re::Ok (op (k)), Re::Err (e) => Re::Err (e)}}} |
| 76 | + |
| 77 | +#[cfg(all(test, feature = "nightly"))] mod test { |
| 78 | + extern crate test; |
| 79 | + |
| 80 | + use super::*; |
| 81 | + |
| 82 | + // cargo bench --features nightly,re |
| 83 | + |
| 84 | + fn foobar (succ: bool) -> Re<bool> { |
| 85 | + if test::black_box (succ) {Re::Ok (true)} else {Re::fail ("!succ")}} |
| 86 | + |
| 87 | + fn bang (succ: bool) -> Re<()> { |
| 88 | + let b = test::black_box (foobar (succ)?); |
| 89 | + assert_eq! (true, b); |
| 90 | + Re::Ok(())} |
| 91 | + |
| 92 | + #[bench] fn err (bm: &mut test::Bencher) { |
| 93 | + bm.iter (|| { |
| 94 | + let e = bang (false) .err().unwrap(); |
| 95 | + assert! (e.starts_with ("re:")); |
| 96 | + assert! (e.ends_with ("] !succ"))})} |
| 97 | + |
| 98 | + #[bench] fn ok (bm: &mut test::Bencher) { |
| 99 | + bm.iter (|| {bang (true) .expect ("!user")})}} |
0 commit comments