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
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
/*!

[![](https://docs.rs/state_machine_future/badge.svg)](https://docs.rs/state_machine_future/) [![](https://img.shields.io/crates/v/state_machine_future.svg)](https://crates.io/crates/state_machine_future) [![](https://img.shields.io/crates/d/state_machine_future.png)](https://crates.io/crates/state_machine_future) [![Build Status](https://travis-ci.org/fitzgen/state_machine_future.png?branch=master)](https://travis-ci.org/fitzgen/state_machine_future)

Easily create type-safe `Future`s from state machines — without the boilerplate.

`state_machine_future` type checks state machines and their state transitions,
and then generates `Future` implementations and typestate<sup>[0][]</sup>
boilerplate for you.

* [Introduction](#introduction)
* [Guide](#guide)
* [Example](#example)
* [Attributes](#attributes)
* [Features](#features)
* [License](#license)
* [Contribution](#contribution)

## Introduction

Most of the time, using `Future` combinators like `map` and `then` are a great
way to describe an asynchronous computation. Other times, the most natural way
to describe the process at hand is a state machine.

When writing state machines in Rust, we want to *leverage the type system to
enforce that only valid state transitions may occur*. To do that, we want
*typestates*<sup>[0][]</sup>: types that represents each state in the state
machine, and methods whose signatures only permit valid state transitions. But
we *also* need an `enum` of every possible state, so we can treat the whole
state machine as a single entity, and implement `Future` for it. But this is
getting to be a *lot* of boilerplate...

Enter `#[derive(StateMachineFuture)]`.

With `#[derive(StateMachineFuture)]`, we describe the states and the possible
transitions between them, and then the custom derive generates:

* A typestate for each state in the state machine.

* A type for the whole state machine that implements `Future`.

* A concrete `start` method that constructs the state machine `Future` for you,
initialized to its start state.

* A state transition polling trait, with a `poll_zee_choo` method for each
non-final state `ZeeChoo`. This trait describes the state machine's valid
transitions, and its methods are called by `Future::poll`.

Then, all *we* need to do is implement the generated state transition polling
trait.

Additionally, `#[derive(StateMachineFuture)]` will statically prevent against
some footguns that can arise when writing state machines:

* Every state is reachable from the start state: *there are no useless states.*

* *There are no states which cannot reach a final state*. These states would
otherwise lead to infinite loops.

* *All state transitions are valid.* Attempting to make an invalid state
transition fails to type check, thanks to the generated typestates.

## Guide

Describe the state machine's states with an `enum` and add
`#[derive(StateMachineFuture)]` to it:

```ignore
#[derive(StateMachineFuture)]
enum MyStateMachine {
    // ...
}
```

There must be one **start** state, which is the initial state upon construction;
one **ready** state, which corresponds to `Future::Item`; and one **error**
state, which corresponds to `Future::Error`.

```ignore
#[derive(StateMachineFuture)]
enum MyStateMachine {
    #[state_machine_future(start)]
    Start,

    // ...

    #[state_machine_future(ready)]
    Ready(MyItem),

    #[state_machine_future(error)]
    Error(MyError),
}
```

Any other variants of the `enum` are intermediate states.

We define which state-to-state transitions are valid with
`#[state_machine_future(transitions(...))]`. This attribute annotates a state
variant, and lists which other states can be transitioned to immediately after
this state.

A final state (either **ready** or **error**) must be reachable from every
intermediate state and the **start** state. Final states are not allowed to have
transitions.

```ignore
#[derive(StateMachineFuture)]
enum MyStateMachine {
    #[state_machine_future(start, transitions(Intermediate))]
    Start,

    #[state_machine_future(transitions(Start, Ready))]
    Intermediate { x: usize, y: usize },

    #[state_machine_future(ready)]
    Ready(MyItem),

    #[state_machine_future(error)]
    Error(MyError),
}
```

From this state machine description, the custom derive generates boilerplate for
us.

For each state, the custom derive creates:

* A typestate for the state. The type's name matches the variant name, for
example the `Intermediate` state variant's typestate is also named `Intermediate`.
The kind of struct type generated matches the variant kind: a unit-style variant
results in a unit struct, a tuple-style variant results in a tuple struct, and a
struct-style variant results in a normal struct with fields.

| State `enum` Variant                              | Generated Typestate            |
| ------------------------------------------------- | ------------------------------ |
| `enum StateMachine { MyState, ... }`              | `struct MyState;`              |
| `enum StateMachine { MyState(bool, usize), ... }` | `struct MyState(bool, usize);` |
| `enum StateMachine { MyState { x: usize }, ... }` | `struct MyState { x: usize };` |

* An `enum` for the possible states that can come after this state. This `enum`
is named `AfterX` where `X` is the state's name. For example, the `Intermediate`
state would get:

```ignore
enum AfterIntermediate {
    Start(Start),
    Ready(Ready),
}
```

Next, for the state machine as a whole, the custom derive generates:

* A state machine `Future` type, which is essentially an `enum` of all the
different typestates. This type is named `BlahFuture` where `Blah` is the name
of the state machine description `enum`. In this example, where the state
machine description is named `MyStateMachine`, the generated state machine
future type would be named `MyStateMachineFuture`.

* A polling trait, `PollBordle` where `Bordle` is this state machine
description's name. For each non-final state `TootWasabi`, this trait has a
method, `poll_toot_wasabi`, which is like `Future::poll` but specialized to the
current state. Each method takes conditional ownership of its state (via
[`RentToOwn`][rent_to_own]) and returns a `futures::Poll<AfterThisState, Error>`
where `Error` is the state machine's error type. This signature *does not allow
invalid state transitions*, which makes attempting an illegal state transition
fail to type check. Here is the `MyStateMachine`'s polling trait, for example:

```ignore
trait PollMyStateMachine {
    fn poll_start<'a>(
        start: &'a mut RentToOwn<'a, Start>,
    ) -> Poll<AfterStart, Error>;

    fn poll_intermediate<'a>(
        intermediate: &'a mut RentToOwn<'a, Intermediate>,
    ) -> Poll<AfterIntermediate, Error>;
}
```

* An implementation of `Future` for that type. This implementation dispatches to
the appropriate polling trait method depending on what state the future is
in:

  * If the `Future` is in the `Start` state, then it uses `<MyStateMachine as
    PollMyStateMachine>::poll_start`.

  * If it is in the `Intermediate` state, then it uses `<MyStateMachine as
    PollMyStateMachine>::poll_intermediate`.

  * Etc...

* A concrete `start` method for the description type (so `MyStateMachine::start`
in this example) which constructs a new state machine `Future` type in its
**start** state for you. This method has a parameter for each field in the
**start** state variant.

| Start `enum` Variant            | Generated `start` Method                                            |
| ------------------------------- | ------------------------------------------------------------------- |
| `MyStart,`                      | `fn start() -> MyStateMachineFuture { ... }`                        |
| `MyStart(bool, usize),`         | `fn start(arg0: bool, arg1: usize) -> MyStateMachineFuture { ... }` |
| `MyStart { x: char, y: bool },` | `fn start(x: char, y: bool) -> MyStateMachineFuture { ... }`        |

Given all those generated types and traits, all we have to do is `impl PollBlah
for Blah` for our state machine `Blah`.

```ignore
impl PollMyStateMachine for MyStateMachine {
    fn poll_start<'a>(
        start: &'a mut RentToOwn<'a, Start>
    ) -> Poll<AfterStart, MyError> {
        // Call `try_ready!(start.inner.poll())` with any inner futures here.
        //
        // If we're ready to transition states, then we should return
        // `Ok(Async::Ready(AfterStart))`. If we are not ready to transition
        // states, return `Ok(Async::NotReady)`. If we encounter an error,
        // return `Err(...)`.
    }

    fn poll_intermediate<'a>(
        intermediate: &'a mut RentToOwn<'a, Intermediate>
    ) -> Poll<AfterIntermediate, MyError> {
        // Same deal as above...
    }
}
```

That's it!

## Example

Here is an example of a simple turn-based game played by two players over HTTP.

```
#[macro_use]
extern crate state_machine_future;

#[macro_use]
extern crate futures;

use futures::{Async, Future, Poll};
use state_machine_future::RentToOwn;
# pub struct Player;
# impl Player {
#     fn request_turn(&self) -> HttpTurnFuture { unimplemented!() }
# }
# pub struct HttpError;
# pub struct HttpInvitationFuture;
# impl Future for HttpInvitationFuture {
#     type Item = ();
#     type Error = HttpError;
#     fn poll(&mut self) -> Poll<(), HttpError> {
#         unimplemented!()
#     }
# }
# pub struct HttpTurnFuture;
# impl Future for HttpTurnFuture {
#     type Item = ();
#     type Error = HttpError;
#     fn poll(&mut self) -> Poll<(), HttpError> {
#         unimplemented!()
#     }
# }
# fn process_turn(_: ()) -> Option<GameResult> { unimplemented!() }
# fn main() {}

/// The result of a game.
pub struct GameResult {
    winner: Player,
    loser: Player,
}

/// Some kind of simple turn based game.
///
/// ```text
///              Invite
///                |
///                |
///                | accept invitation
///                |
///                |
///                V
///           WaitingForTurn --------+
///                |   ^             |
///                |   |             | receive turn
///                |   |             |
///                |   +-------------+
/// game concludes |
///                |
///                |
///                |
///                V
///            Finished
/// ```
#[derive(StateMachineFuture)]
enum Game {
    /// The game begins with an invitation to play from one player to another.
    ///
    /// Once the invited player accepts the invitation over HTTP, then we will
    /// switch states into playing the game, waiting to recieve each turn.
    #[state_machine_future(start, transitions(WaitingForTurn))]
    Invite {
        invitation: HttpInvitationFuture,
        from: Player,
        to: Player,
    },

    // We are waiting on a turn.
    //
    // Upon receiving it, if the game is now complete, then we go to the
    // `Finished` state. Otherwise, we give the other player a turn.
    #[state_machine_future(transitions(WaitingForTurn, Finished))]
    WaitingForTurn {
        turn: HttpTurnFuture,
        active: Player,
        idle: Player,
    },

    // The game is finished with a `GameResult`.
    //
    // The `GameResult` becomes the `Future::Item`.
    #[state_machine_future(ready)]
    Finished(GameResult),

    // Any state transition can implicitly go to this error state if we get an
    // `HttpError` while waiting on a turn or invitation acceptance.
    //
    // This `HttpError` is used as the `Future::Error`.
    #[state_machine_future(error)]
    Error(HttpError),
}

// Now, we implement the generated state transition polling trait for our state
// machine description type.

impl PollGame for Game {
    fn poll_invite<'a>(
        invite: &'a mut RentToOwn<'a, Invite>
    ) -> Poll<AfterInvite, HttpError> {
        // See if the invitation has been accepted. If not, this will early
        // return with `Ok(Async::NotReady)` or propagate any HTTP errors.
        try_ready!(invite.invitation.poll());

        // We're ready to transition into the `WaitingForTurn` state, so take
        // ownership of the `Invite` and then construct and return the new
        // state.
        let invite = invite.take();
        Ok(Async::Ready(AfterInvite::WaitingForTurn(WaitingForTurn {
            turn: invite.from.request_turn(),
            active: invite.from,
            idle: invite.to,
        })))
    }

    fn poll_waiting_for_turn<'a>(
        waiting: &'a mut RentToOwn<'a, WaitingForTurn>
    ) -> Poll<AfterWaitingForTurn, HttpError> {
        // See if the next turn has arrived over HTTP. Again, this will early
        // return `Ok(Async::NotReady)` if the turn hasn't arrived yet, and
        // propagate any HTTP errors that we might encounter.
        let turn = try_ready!(waiting.turn.poll());

        // Ok, we have a new turn. Take ownership of the `WaitingForTurn` state,
        // process the turn and if the game is over, then transition to the
        // `Finished` state, otherwise swap which player we need a new turn from
        // and request the turn over HTTP.
        let waiting = waiting.take();
        if let Some(game_result) = process_turn(turn) {
            Ok(Async::Ready(AfterWaitingForTurn::Finished(Finished(game_result))))
        } else {
            Ok(Async::Ready(AfterWaitingForTurn::WaitingForTurn(WaitingForTurn {
                turn: waiting.idle.request_turn(),
                active: waiting.idle,
                idle: waiting.active,
            })))
        }
    }
}

# struct TokioHandle;
# impl TokioHandle { fn spawn<T>(&self, _: T) {} }
# fn get_some_player() -> Player { unimplemented!() }
# fn get_another_player() -> Player { unimplemented!() }
# fn invite(_: &Player, _: &Player) -> HttpInvitationFuture { unimplemented!() }
// To spawn a new `Game` as a `Future` on whatever executor we're using (for
// example `tokio`), we use `Game::start` to construct the `Future` in its start
// state and then pass it to the executor.
fn spawn_game(handle: TokioHandle) {
    let from = get_some_player();
    let to = get_another_player();
    let invitation = invite(&from, &to);
    let future = Game::start(invitation, from, to);
    handle.spawn(future)
}
```

## Attributes

This is a list of all of the attributes used by `state_machine_future`:

* `#[derive(FutureStateMachine)]`: Placed on an `enum` that describes a state
machine.

* `#[future_state_machine(derive(Clone, Debug, ...))]`: Placed on the `enum`
that describes the state machine. This attribute describes which
`#[derive(...)]`s to place on the generated `Future` type.

* `#[future_state_machine(start)]`: Used on a variant of the state machine
description `enum`. There must be exactly one variant with this attribute. This
describes the initial starting state. The generated `start` method has a
parameter for each field in this variant.

* `#[future_state_machine(ready)]`: Used on a variant of the state machine
description `enum`. There must be exactly one variant with this attribute. It
must be a tuple-style variant with one field, for example `Ready(MyItemType)`.
The generated `Future` implementation uses the field's type as `Future::Item`.

* `#[future_state_machine(error)]`: Used on a variant of the state machine
description `enum`. There must be exactly one variant with this attribute. It
must be a tuple-style variant with one field, for example `Error(MyError)`.  The
generated `Future` implementation uses the field's type as `Future::Error`.

* `#[future_state_machine(transitions(OtherState, AnotherState, ...))]`: Used on
a variant of the state machine description `enum`. Describes the states that
this one can transition to.

## Features

Here are the `cargo` features that you can enable:

* `debug_code_generation`: Prints the code generated by
`#[derive(StateMachineFuture)]` to `stdout` for debugging purposes.

## License

Licensed under either of

 * [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0)

 * [MIT license](http://opensource.org/licenses/MIT)

at your option.

## Contribution

See
[CONTRIBUTING.md](https://github.com/fitzgen/state_machine_future/blob/master/CONTRIBUTING.md)
for hacking.

Unless you explicitly state otherwise, any contribution intentionally submitted
for inclusion in the work by you, as defined in the Apache-2.0 license, shall be
dual licensed as above, without any additional terms or conditions.


[0]: https://en.wikipedia.org/wiki/Typestate_analysis

[rent_to_own]: https://crates.io/crates/rent_to_own

 */

#![deny(missing_docs)]
#![deny(missing_debug_implementations)]

extern crate futures;
extern crate rent_to_own;

// Re-export the custom derive. This allows people to depend only on this crate
// directly, not both this one and `derive_state_machine_future`.
#[allow(unused_imports)]
#[macro_use]
extern crate derive_state_machine_future;
pub use derive_state_machine_future::*;

mod compile_fail_tests;

/// Re-export of `rent_to_own::RentToOwn`.
pub type RentToOwn<'a, T> = rent_to_own::RentToOwn<'a, T>;

/// A trait that links an `enum` with `#[derive(StateMachineFuture)]` to its
/// generated type that implements `Future`.
pub trait StateMachineFuture {
    /// The generated `Future` type for this state machine.
    type Future: futures::Future;
}