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
193
194
195
196
197
198
199
//! This library implements parts Saga system for execution and rollback
//! consectual steps.
//!
//! The basic concept of this installation is that each Saga is made of
//! a number of Adventures.
//! Each Adventure has a forward and backwards step.
//!
//! When a forward step returns a Failure, the state of that
//! is passed to the backwards step of this and all prior ones
//!
//! ```
//! // f1 -> f2 -> f3
//! //             | error
//! //             v
//! // b1 <- b2 <- b3
//! ```
//!
//! An simple example of a saga would be the following:
//!
//! ```
//!     use aud::Adventure;
//!     use aud::Failure;
//!     use aud::Saga;
//!     use std::error::Error;
//!     use std::fmt;
//!
//!
//!    #[derive(Debug)]
//!    pub struct StupidError {
//!        stupid: bool,
//!    }
//!
//!     impl Error for StupidError {
//!         fn description(&self) -> &str {
//!             "stupid error"
//!         }
//!     }
//!     impl fmt::Display for StupidError {
//!         fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
//!             write!(f, "is stupid: {}", self.stupid)
//!         }
//!     }
//!     fn inc2(i: i32) -> Result<i32, Failure<i32>> {
//!             Ok(i + 1)
//!     }
//!     fn dec(i: i32) -> i32 {
//!         i - 1
//!     }
//!     fn main() {
//!         let saga = Saga::new(vec![
//!             Adventure::new(inc2,dec),
//!             Adventure::new(inc2,dec),
//!         ]);
//!         match saga.tell(0) {
//!             Ok(res) => assert!(res == 2),
//!             Err(_) => unimplemented!(),
//!         }
//!     }
//! ```

#![deny(trivial_numeric_casts,
        missing_docs,
        unstable_features,
        unused_import_braces,
)]
use std::error::Error;

/// A sage of many adventures that can be told.
pub struct Saga<T> {
    adventures: Vec<Adventure<T>>,
}

impl<T> Saga<T> {
    /// Creates a new saga from a vector of adventures
    pub fn new(adventures: Vec<Adventure<T>>) -> Self {
        Saga { adventures: adventures }
    }
    /// Tells a saga, reverts on failure and returns either the result or error
    pub fn tell(self: &Self, acc: T) -> Result<T, Failure<T>> {
        tell_(&self.adventures, 0, acc)
    }
}

/// An adventure that can can forward succeed or fail and be reverted.
/// Make sure that a failure includes enough info for THIS step itsel
/// to be reverted
pub struct Adventure<T> {
    forward: fn(T) -> Result<T, Failure<T>>,
    backward: fn(T) -> T,
}

impl<T> Adventure<T> {
    /// Creates a new adventure with a forward and backward step
    pub fn new(forward: fn(T) -> Result<T, Failure<T>>, backward: fn(T) -> T) -> Self {
        Adventure { forward, backward }
    }
    fn forward(self: &Adventure<T>, acc: T) -> Result<T, Failure<T>> {
        let f = self.forward;
        f(acc)
    }
    fn backward(self: &Adventure<T>, acc: T) -> T {
        let f = self.backward;
        f(acc)
    }
}

/// A simple failure that can return an error along with the new state.
pub struct Failure<T> {
    error: Box<Error>,
    state: T,
}

impl<T> Failure<T> {
    /// Creates a new failure from a state and an error
    pub fn new(state: T, error: Box<Error>) -> Self {
        Failure { state, error }
    }
    /// Truns the Failure into an Err
    pub fn to_error(self) -> Box<Error> {
        self.error
    }
}

fn tell_<T>(saga: &Vec<Adventure<T>>, i: usize, acc: T) -> Result<T, Failure<T>> {
    if i >= saga.len() {
        Ok(acc)
    } else {
        match saga[i].forward(acc) {
            Ok(acc1) => tell_(saga, i + 1, acc1),
            Err(Failure { state: acc1, error }) => Err(revert(saga, error, i, acc1)),
        }
    }
}

fn revert<T>(saga: &Vec<Adventure<T>>, error: Box<Error>, i: usize, acc: T) -> Failure<T> {
    let acc1 = saga[i].backward(acc);
    if i == 0 {
        Failure { error, state: acc1 }
    } else {
        revert(saga, error, i - 1, acc1)
    }
}

#[cfg(test)]
mod tests {
    use Adventure;
    use Failure;
    use Saga;
    use std::error::Error;
    use std::fmt;

    #[derive(Debug)]
    pub struct StupidError {
        stupid: bool,
    }

    impl Error for StupidError {
        fn description(&self) -> &str {
            "stupid error"
        }
    }
    impl fmt::Display for StupidError {
        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
            write!(f, "is stupid: {}", self.stupid)
        }
    }
    fn inc2(i: i32) -> Result<i32, Failure<i32>> {
        if i >= 2 {
            Err(Failure::new(i + 1, Box::new(StupidError { stupid: true })))
        } else {
            Ok(i + 1)
        }
    }
    fn dec(i: i32) -> i32 {
        i - 1
    }
    #[test]
    fn good_sage() {
        let saga = Saga::new(vec![Adventure::new(inc2, dec), Adventure::new(inc2, dec)]);
        match saga.tell(0) {
            Ok(res) => assert!(res == 2),
            Err(_) => unimplemented!(),
        }
    }
    #[test]
    fn bad_sage() {
        let saga = Saga::new(vec![
            Adventure::new(inc2, dec),
            Adventure::new(inc2, dec),
            Adventure::new(inc2, dec),
        ]);
        match saga.tell(0) {
            Ok(_) => unimplemented!(),
            Err(Failure { state: res, .. }) => assert_eq!(res, 0),
        }

    }

}