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
//! [<img alt="github" src="https://img.shields.io/badge/github-rustype/typestate-8da0cb?style=flat-square&logo=github">](https://github.com/rustype/typestate-rs)
//! [<img alt="docs" src="https://img.shields.io/badge/docs.rs-typestate-success?style=flat-square">](https://docs.rs/typestate)
//! [<img alt="crates" src="https://img.shields.io/crates/v/typestate?style=flat-square">](https://crates.io/crates/typestate)
//!
//! Are you frustrated with `IllegalStateException`s in Java?
//!
//! Typestates allow you to define *safe* usage protocols for your objects.
//! The compiler will help you on your journey and disallow errors on given states.
//! You will no longer be able to try and read from closed streams.
//!
//! `#[typestate]` builds on ideas from the [`state_machine_future`](https://github.com/fitzgen/state_machine_future) crate.
//! If typestates are so useful, why not use them with limit them to `Future`s?
//!
//! ### Typestates in Rust
//!
//! Typestates are not a new concept to Rust.
//! There are several blog posts on the subject
//! [[1](https://yoric.github.io/post/rust-typestate/),
//! [2](http://cliffle.com/blog/rust-typestate/),
//! [3](https://rustype.github.io/notes/notes/rust-typestate-series/rust-typestate-index)]
//! as well as a [chapter](https://docs.rust-embedded.org/book/static-guarantees/typestate-programming.html) in *The Embedded Rust Book*.
//!
//! In short, we can write typestates by hand, we add some generics here and there,
//! declare them as a "*state*" and in the end we can keep living our lives with our new state machine.
//!
//! This approach however is *error-prone* and *verbose* (especially with bigger automata).
//! It also provides *no* guarantees about the automata, unless of course, you designed and tested the design previously.
//!
//! As programmers, we want to automate this cumbersome job and to do so, we use Rust's powerful procedural macros!
//!
//! ## Basic Guide
//!
//! Consider we are tasked with building the firmware for a traffic light,
//! we can turn it on and off and cycle between Green, Yellow and Red.
//!
//! We first declare a module with the `#[typestate]` macro attached to it.
//! ```rust,ignore
//! #[typestate]
//! mod traffic_light {}
//! ```
//!
//! This of course does nothing, in fact it will provide you an error,
//! saying that we haven't declared an *automaton*.
//!
//! And so, our next task is to do that.
//! Inside our `traffic_light` module we declare a structure annotated with `#[automaton]`.
//! ```rust,ignore
//! #[automaton]
//! pub struct TrafficLight;
//! ```
//!
//! Our next step is to declare the states.
//! We declare three empty structures annotated with `"[state]`.
//! ```rust,ignore
//! #[state] pub struct Green;
//! #[state] pub struct Yellow;
//! #[state] pub struct Red;
//! ```
//!
//! So far so good, however some errors should appear, regarding the lack of initial and final states.
//!
//! To declare initial and final states we need to see them as describable by transitions.
//! Whenever an object is created, the method that created leaves the object in the *initial* state.
//! Equally, whenever a method consumes an object and does not return it (or a similar version of it),
//! it made the object reach the *final* state.
//!
//! With this in mind we can lay down the following rules:
//! - Functions that *do not* take a valid state (i.e. `self`) and return a valid state, describe an initial state.
//! - Functions that take a valid state (i.e. `self`) and *do not* return a valid state, describe a final state.
//!
//! So we write the following function signatures:
//! ```rust,ignore
//! fn turn_on() -> Red;
//! fn turn_off(self);
//! ```
//!
//! However, these are *free* functions, meaning that `self` relates to nothing.
//! To attach them to a state we wrap them around a `trait` with the name of the state they are supposed to be attached to.
//! So our previous example becomes:
//! ```rust,ignore
//! trait Red {
//!     fn turn_on() -> Red;
//!     fn turn_off(self);
//! }
//! ```
//!
//! *Before we go further, a quick review:*
//! > - The module is annotated with `#[typestate]` enabling the DSL.
//! > - To declare the main automaton we attach `#[automaton]` to a structure.
//! > - The states are declared by attaching `#[state]`.
//! > - State functions are declared through traits that share the same name.
//! > - Initial and final states are declared by functions with a "special" signature.
//!
//! Finally, we need to address how states transition between each other.
//! An astute reader might have inferred that we can consume one state and return another,
//! such reader would be 100% correct.
//!
//! For example, to transition between the `Red` state and the `Green` we do:
//! ```rust,ignore
//! trait Red {
//!     fn to_green(self) -> Green;
//! }
//! ```
//!
//! Building on this we can finish the other states:
//! ```rust,ignore
//! pub trait Green {
//!     fn to_yellow(self) -> Yellow;
//! }
//!
//! pub trait Yellow {
//!     fn to_red(self) -> Red;
//! }
//!
//! pub trait Red {
//!     fn to_green(self) -> Green;
//!     fn turn_on() -> Red;
//!     fn turn_off(self);
//! }
//! ```
//!
//! And the full code becomes:
//!
//! ```rust,ignore
//! #[typestate]
//! mod traffic_light {
//!     #[automaton]
//!     pub struct TrafficLight {
//!         pub cycles: u64,
//!     }
//!
//!     #[state] pub struct Green;
//!     #[state] pub struct Yellow;
//!     #[state] pub struct Red;
//!
//!     pub trait Green {
//!         fn to_yellow(self) -> Yellow;
//!     }
//!
//!     pub trait Yellow {
//!         fn to_red(self) -> Red;
//!     }
//!
//!     pub trait Red {
//!         fn to_green(self) -> Green;
//!         fn turn_on() -> Red;
//!         fn turn_off(self);
//!     }
//! }
//! ```
//!
//! The code above will generate:
//! - Expand the main structure with a `state: State` field.
//! - A sealed trait which disallows states from being added *externally*.
//! - Traits for each state, providing the described functions.
//!
//! ## Advanced Guide
//!
//! There are some features which may be helpful when describing a typestate.
//! There are two main features that weren't discussed yet.
//!
//! ### Self-transitioning functions
//! Putting it simply, states may require to mutate themselves without transitioning, or maybe we require a simple getter.
//! To declare methods for that purpose, we can use functions that take references (mutable or not) to `self`.
//!
//! Consider the following example where we have a flag that can be up or not.
//! We have two functions, one checks if the flag is up, the other, sets the flag up.
//!
//! ```rust,ignore
//! #[state] struct Flag {
//!     up: bool
//! }
//!
//! impl Flag {
//!     fn is_up(&self) -> bool;
//!     fn set_up(&mut self);
//! }
//! ```
//!
//! As these functions do not change the typestate state,
//! they transition back to the current state.
//!
//! ### Non-deterministic transitions
//! Consider that a typestate relies on an external component that can fail, to model that, one would use `Result<T>`.
//! However, we need our typestate to transition between known states, so we declare two things:
//! - An `Error` state along with the other states.
//! - An `enum` to represent the bifurcation of states.
//!
//! ```rust,ignore
//! #[state] struct Error {
//!     message: String
//! }
//!
//! enum OperationResult {
//!     State, Error
//! }
//! ```
//!
//! Inside the enumeration there can only be other valid states and only `Unit` style variants are supported.
//!
//! ## Attributes
//!
//! This is the list of attributes that can be used along `#[typestate]`:
//! - `#[typestate]`: the main attribute macro, without attribute parameters.
//! - `#[typestate(enumerate = "...")]`: this option makes the macro generate an additional `enum`,
//!   the `enum` enables working with variables and structures "generic" to the state.
//!   - The parameter can be declared *with* or *without* a string literal, if declared with the string,
//!     that string will be used as identifier to the `enum`.
//!   - If the parameter is used with an *empty string* or *without* a string, the default behavior is to prepend an `E` to the
//! - `#[typestate(state_constructors = "...")`: this option generates basic constructors for states with fields.
//!
//! ## Features
//! The cargo features you can enable:
//! - `debug_dot` will generate a `.dot` file of your state machine.
//!   - This feature can be customized through the following environment variables (taken from the [DOT documentation](https://graphviz.org/doc/info/attrs.html)):
//!     - `DOT_PAD` - Specifies how much, in inches, to extend the drawing area around the minimal area needed to draw the graph.
//!     - `DOT_NODESEP` - In `dot`, `nodesep` specifies the minimum space between two adjacent nodes in the same rank, in inches.
//!     - `DOT_RANKSEP` - In `dot`, sets the desired rank separation, in inches.
//! - `debug_plantuml` will generate a PlantUML state diagram (`.uml` file) of your state machine.

pub extern crate typestate_proc_macro;

pub use ::typestate_proc_macro::typestate;

#[doc(hidden)]
pub mod __private__ {
    #[cfg(feature = "mermaid-docs")]
    pub use ::aquamarine;
}