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
/// Extensions to Result
pub trait ResultExt<T, E>: Sized {
  /// Alias for [`Result.and_then`]
  fn bind<R>(self, f: impl FnOnce(T) -> Result<R, E>) -> Result<R, E>;

  /// Swap the Ok and Err variants
  fn swap(self) -> Result<E, T>;

  /// Allows turning an Err back into Ok by binding on the Err variant
  fn recover<R>(self, f: impl FnOnce(E) -> Result<T, R>) -> Result<T, R>;

  /// Attempt to perform some fallible IO
  fn try_perform(self, f: impl FnOnce(&T) -> Result<(), E>) -> Result<T, E>;

  /// Perform some IO when this Result is Err
  fn perform_err(self, f: impl FnOnce(&E) -> ()) -> Result<T, E>;

  /// Perform some IO when this Result is Ok
  fn perform(self, f: impl FnOnce(&T) -> ()) -> Result<T, E>;

  /// Perform some IO mutating the data contained in the Ok of this Result
  fn perform_mut(self, f: impl FnOnce(&mut T) -> ()) -> Result<T, E>;

  /// Test the data in Ok and turn it into an Err if it doesn't pass a predicate
  fn filter(self, pred: impl FnOnce(&T) -> bool, on_fail: impl FnOnce(&T) -> E) -> Result<T, E>;

  /// Do some fallible IO that resolves in a value and combine Oks
  fn tupled<R>(self, f: impl FnOnce(&T) -> Result<R, E>) -> Result<(T, R), E> {
    self.bind(|t| f(&t).map(|r| (t, r)))
  }

  /// Boolean AND
  fn two<B>(a: Result<T, E>, b: Result<B, E>) -> Result<(T, B), E> {
    a.and_then(|a| b.map(|b| (a, b)))
  }
}

impl<T, E> ResultExt<T, E> for Result<T, E> {
  fn bind<R>(self, f: impl FnOnce(T) -> Result<R, E>) -> Result<R, E> {
    self.and_then(f)
  }

  fn recover<R>(self, f: impl FnOnce(E) -> Result<T, R>) -> Result<T, R> {
    match self {
      | Ok(t) => Ok(t),
      | Err(e) => f(e),
    }
  }

  fn try_perform(self, f: impl FnOnce(&T) -> Result<(), E>) -> Result<T, E> {
    self.and_then(|t| f(&t).map(|_| t))
  }

  fn perform(self, f: impl FnOnce(&T) -> ()) -> Result<T, E> {
    self.map(|t| {
          f(&t);
          t
        })
  }

  fn perform_err(self, f: impl FnOnce(&E) -> ()) -> Result<T, E> {
    self.map_err(|t| {
          f(&t);
          t
        })
  }

  fn perform_mut(self, f: impl FnOnce(&mut T) -> ()) -> Result<T, E> {
    self.map(|mut t| {
          f(&mut t);
          t
        })
  }

  fn filter(self, pred: impl FnOnce(&T) -> bool, on_fail: impl FnOnce(&T) -> E) -> Result<T, E> {
    self.bind(|t| if pred(&t) { Err(on_fail(&t)) } else { Ok(t) })
  }

  fn swap(self) -> Result<E, T> {
    match self {
      | Ok(err) => Err(err),
      | Err(ok) => Ok(ok),
    }
  }
}