Orchestration in Rust
Tags: rust, orchestration, patterns
Published: 2020-11-07 18:59:57

Porting patterns from Object Oriented concepts in Rust do not always work, but there are certain cases they not only work but sometimes they are easier to follow when compared with the original OOP implementation.

Here I am going to demonstrate the concept of orchestration in Rust.

Taking a step back orchestration resembles vaguely of redux, with the key difference that a reducer has not only has to deal with the state but a payload as well.

Orchestration is basically a pipeline for which the previous state is passed in the next operation. The key thing in orchestration is the ability to pause and resume.

I classify this example as an intermediate one, but not that hard to follow.

We will start by talking about and defining our State object;

```rust
#[derive(Debug, Clone)]
pub struct State {
    pub proceed: bool,
    pub outcome: f32,
    pub stage: Vec<bool>,
}
```
Our state has the following
* proceed: which marks each stage as successful or not, marking proceed as false, that will stop the execution of the remaining stages
* outcome: result of each stage
* stage: record the outcome of the stage

Then we will define the Orchestrate trait

```rust
trait Orchestrate {
    fn execute(self, state: State) -> State;
}
```

and we will implement the Orchestrate trait on a Vector of Functions

```rust
impl Orchestrate for Vec<fn(State) -> Result<State, Error>> {
     fn execute(self, state: State) -> State {
        self.iter().enumerate().fold(state, |output, (i, func)| {
            let new_state = output.clone();
            if new_state.stage.len() > i {
                if new_state.stage[i] {
                    return new_state;
                } else {
                    let mut next_state = func(new_state).unwrap();
                    next_state.stage[i] = next_state.proceed;
                    return next_state;
                }
            }
            let mut next_state = func(new_state).unwrap();
            next_state.stage.push(next_state.proceed);
            return next_state;
        })
    }
}
```

It's already easy to see where this is going.  
* we received the array of functions
* for each execution we check if the operation has been completed in a previous execution (stage)
* we either execute the stage or ignore it

Next we will define 3 simple operations we want to perform in this orchestration. How are they going to be used, one would wonder.

```rust 
fn pow2(n: f32) -> f32 {
    n.powf(2.0)
}

fn pow3(n: f32) -> f32 {
    n.powf(3.0)
}

fn sqrt(n: f32) -> f32 {
    n.sqrt()
}
```

And now my favourite part, instead of just creating a function that gets a State and returns a state and has the pause resume logic in it I am going to create a macro that creates the functions by accepting a function as an argument. 

```rust
macro_rules! state_function {
    ( $func:expr ) => {{
        pub fn state_fn(c: State) -> Result<State, Error> {
            let stage: Vec<bool> = c.stage.to_vec();
            if c.proceed == false {
                Ok(State {
                    proceed: false,
                    outcome: c.outcome,
                    stage: stage,
                })
            } else {
                let mut rng = rand::thread_rng();
                let y: bool = rng.gen();
                Ok(State {
                    proceed: y,
                    outcome: $func(c.outcome),
                    stage: stage,
                })
            }
        }

        state_fn
    }};
}
```

* If proceed is false, don't do anything
* else return the state with the new outcome.

And to spice things a bit up I used a random boolean, to disrupt the processing and make things a bit more interesting.

So finally on our main function,
Step 1: we create our functions
```rust 
    let a: fn(State) -> Result<State, Error> = state_function!(pow2);
    let b: fn(State) -> Result<State, Error> = state_function!(pow3);
    let c: fn(State) -> Result<State, Error> = state_function!(sqrt);
```
Step 2: we define our chain

```rust
    let chain: Vec<fn(State) -> Result<State, Error>> =  vec![a, b, c];
```
Step 3: we execute our chain
```rust
    let result = chain
        .execute(State {
            proceed: true,
            outcome: 6.,
            stage: Vec::<bool>::new(),
        });

    println!("{:?}", result);
```
BONUS
Step 4: we execute our chain with assuming some steps have been completed

```rust
    let result = chain
        .execute(State {
            proceed: true,
            outcome: 6.,
            stage: vec![true, false, false],
        });

    println!("{:?}", result);
```

I hope you enjoyed it... :D

Update: a generic version of this pattern can be found here https://github.com/elasticrash/orchestrator