Skip to content

Commit 32d7513

Browse files
authored
Merge pull request #69 from korken89/release/0.7.0
Release/0.7.0
2 parents 6a2188c + 610ce45 commit 32d7513

File tree

10 files changed

+177
-63
lines changed

10 files changed

+177
-63
lines changed

CHANGELOG.md

+4-1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
77

88
## [Unreleased]
99

10+
## [0.7.0] - 2024-07-03
11+
1012
### Added
1113

1214
- Add support for async guards and actions
@@ -172,7 +174,8 @@ a long list of states to go through.
172174
* Support for data in states
173175
* Change log added
174176

175-
[Unreleased]: https://github.com/korken89/smlang-rs/compare/v0.6.0...master
177+
[Unreleased]: https://github.com/korken89/smlang-rs/compare/v0.7.0...master
178+
[v0.7.0]: https://github.com/korken89/smlang-rs/compare/v0.6.0...v0.7.0
176179
[v0.6.0]: https://github.com/korken89/smlang-rs/compare/v0.5.1...v0.6.0
177180
[v0.5.1]: https://github.com/korken89/smlang-rs/compare/v0.5.0...v0.5.1
178181
[v0.5.0]: https://github.com/korken89/smlang-rs/compare/v0.4.2...v0.5.0

Cargo.toml

+2-3
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,12 @@ description = "A no-std state machine language DSL"
66
keywords = ["dsl", "statemachine"]
77
license = "MIT OR Apache-2.0"
88
repository = "https://github.com/korken89/smlang-rs"
9-
version = "0.6.0"
9+
version = "0.7.0"
1010
edition = "2018"
1111
readme = "README.md"
1212

1313
[dependencies]
14-
smlang-macros = { path = "macros", version = "0.6.0" }
15-
async-trait = "0.1"
14+
smlang-macros = { path = "macros", version = "0.7.0" }
1615

1716
[dev-dependencies]
1817
smol = "1"

README.md

+55-11
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,16 @@
55

66
> A state machine language DSL based on the syntax of [Boost-SML](https://boost-ext.github.io/sml/).
77
8-
## Aim
8+
`smlang` is a procedural macro library creating a state machine language DSL, whose aim to facilitate the
9+
use of state machines, as they quite fast can become overly complicated to write and get an
10+
overview of.
911

10-
The aim of this DSL is to facilitate the use of state machines, as they quite fast can become overly complicated to write and get an overview of.
12+
The library supports both `async` and non-`async` code.
1113

1214
## Transition DSL
1315

14-
The DSL is defined as follows:
16+
Below is a sample of the DSL. For a full description of the `statemachine` macro, please reference
17+
the [DSL document](docs/dsl.md).
1518

1619
```rust
1720
statemachine!{
@@ -23,7 +26,9 @@ statemachine!{
2326
}
2427
```
2528

26-
Where `guard` and `action` are optional and can be left out. A `guard` is a function which returns `true` if the state transition should happen, and `false` if the transition should not happen, while `action` are functions that are run during the transition which are guaranteed to finish before entering the new state.
29+
Where `guard` and `action` are optional and can be left out. A `guard` is a function which returns
30+
`Ok(true)` if the state transition should happen - otherwise, the transition should not happen.
31+
The `action` functions are run during the state machine transition.
2732

2833
> This implies that any state machine must be written as a list of transitions.
2934
@@ -89,7 +94,9 @@ in the order they appear in the state machine definition, will be selected.
8994
### State machine context
9095

9196
The state machine needs a context to be defined.
92-
The `StateMachineContext` is generated from the `statemachine!` proc-macro and is what implements guards and actions, and data that is available in all states within the state machine and persists between state transitions:
97+
The `StateMachineContext` is generated from the `statemachine!` proc-macro and is what implements
98+
guards and actions, and data that is available in all states within the state machine and persists
99+
between state transitions:
93100

94101
```rust
95102
statemachine!{
@@ -112,6 +119,7 @@ fn main() {
112119

113120
See example `examples/context.rs` for a usage example.
114121

122+
115123
### State data
116124

117125
Any state may have some data associated with it:
@@ -202,12 +210,49 @@ See example `examples/guard_action_syntax.rs` for a usage-example.
202210

203211
### Async Guard and Action
204212

213+
Guards and actions may both be optionally `async`:
214+
```rust
215+
use smlang::{async_trait, statemachine};
216+
217+
statemachine! {
218+
transitions: {
219+
*State1 + Event1 [guard1] / async action1 = State2,
220+
State2 + Event2 [async guard2] / action2 = State3,
221+
}
222+
}
223+
224+
225+
pub struct Context {
226+
// ...
227+
}
228+
229+
impl StateMachineContext for Context {
230+
async fn action1(&mut self) -> () {
231+
// ...
232+
}
233+
234+
async fn guard2(&mut self) -> Result<(), ()> {
235+
// ...
236+
}
237+
238+
fn guard1(&mut self) -> Result<(), ()> {
239+
// ...
240+
}
241+
242+
fn action2(&mut self) -> () {
243+
// ...
244+
}
245+
}
246+
```
247+
248+
205249
See example `examples/async.rs` for a usage-example.
206250

207251
## State Machine Examples
208252

209-
Here are some examples of state machines converted from UML to the State Machine Language DSL. Runnable versions of each example is available in the `examples` folder.
210-
The `.png`s are generated with the `graphviz` feature.
253+
Here are some examples of state machines converted from UML to the State Machine Language DSL.
254+
Runnable versions of each example is available in the `examples` folder. The `.png`s are generated
255+
with the `graphviz` feature.
211256

212257
### Linear state machine
213258

@@ -315,6 +360,7 @@ List of contributors in alphabetical order:
315360

316361
* Emil Fresk ([@korken89](https://github.com/korken89))
317362
* Mathias Koch ([@MathiasKoch](https://github.com/MathiasKoch))
363+
* Ryan Summers ([@ryan-summers](https://github.com/ryan-summers))
318364
* Donny Zimmanck ([@dzimmanck](https://github.com/dzimmanck))
319365

320366
---
@@ -323,10 +369,8 @@ List of contributors in alphabetical order:
323369

324370
Licensed under either of
325371

326-
- Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or
327-
http://www.apache.org/licenses/LICENSE-2.0)
328-
329-
- MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
372+
- Apache License, Version 2.0 [LICENSE-APACHE](LICENSE-APACHE) or <http://www.apache.org/licenses/LICENSE-2.0>
373+
- MIT license [LICENSE-MIT](LICENSE-MIT) or <http://opensource.org/licenses/MIT>
330374

331375
at your option.
332376

docs/dsl.md

+57
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
## Transition DSL
2+
3+
The state machine macro DSL is defined as follows:
4+
5+
```rust
6+
use smlang::statemachine;
7+
8+
statemachine!{
9+
// [Optional] An optional prefix to name the generated state machine trait code. This
10+
// can be used to allow multiple state machines to exist in the same source
11+
// file. The generated trait and types are `<name>States`, `<name>Events`,
12+
// and `<name>StateMachine` respectively.
13+
name: Name,
14+
15+
// [Optional] Can be used if a temporary context is needed within the state machine
16+
// API. When specified, the temporary context is provided in
17+
// `StateMachine::process_event()` and is exposed in guards and actions as
18+
// the second argument.
19+
temporary_context: u32,
20+
21+
// [Optional] Can be optionally specified to add a new `type Error` to the
22+
// generated `StateMachineContext` trait to allow guards to return a custom
23+
// error type instead of `()`.
24+
custom_guard_error: false,
25+
26+
// [Optional] A list of derive names for the generated `States` and `Events`
27+
// enumerations respectively. For example, to `#[derive(Debug)]`, these
28+
// would both be specified as `[Debug]`.
29+
derive_states: [],
30+
derive_events: [],
31+
32+
transitions: {
33+
// * denotes the starting state
34+
*StartState + Event1 [ guard1] / action1 = DstState1,
35+
36+
// Guards and actions can be async functions.
37+
SrcState2 + Event2 [ async guard2 ] / async action2 = DstState2,
38+
39+
// Pattern matching can be used to support multiple states with the same
40+
// transition event.
41+
StartState | SrcState2 + Event3 [ guard3] / action3 = DstState3,
42+
43+
// ..or wildcarding can be used to allow all states to share a
44+
// transition event.
45+
_ + Event4 = DstState4,
46+
47+
// States can contain data
48+
StateWithData(u32) + Event = DstState5,
49+
StateWithOtherData(&'a u32) + Event = DstState5,
50+
51+
// Guards can be logically combined using `!`, `||`, and `&&`.
52+
SrcState6 + Event6 [ async guard6 || other_guard6 ] / action6 = DstState6,
53+
SrcState7 + Event7 [ async guard7 && !other_guard7 ] / action7 = DstState7,
54+
}
55+
// ...
56+
}
57+
```

examples/async.rs

+1-2
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
55
#![deny(missing_docs)]
66

7-
use smlang::{async_trait, statemachine};
7+
use smlang::statemachine;
88

99
statemachine! {
1010
transitions: {
@@ -20,7 +20,6 @@ pub struct Context {
2020
done: bool,
2121
}
2222

23-
#[async_trait]
2423
impl StateMachineContext for Context {
2524
fn guard3(&self) -> Result<bool, ()> {
2625
println!("`guard3` called from async context");

examples/named_async.rs

+1-2
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
55
#![deny(missing_docs)]
66

7-
use smlang::{async_trait, statemachine};
7+
use smlang::statemachine;
88

99
statemachine! {
1010
name: AsyncSimple,
@@ -21,7 +21,6 @@ pub struct Context {
2121
done: bool,
2222
}
2323

24-
#[async_trait]
2524
impl AsyncSimpleStateMachineContext for Context {
2625
fn guard1(&self) -> Result<bool, ()> {
2726
println!("`guard1` called from sync context");

macros/Cargo.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ description = "Procedual macros for the smlang crate"
66
keywords = ["dsl", "statemachine"]
77
license = "MIT OR Apache-2.0"
88
repository = "https://github.com/korken89/smlang-rs"
9-
version = "0.6.0"
9+
version = "0.7.0"
1010
edition = "2018"
1111
readme = "../README.md"
1212

macros/src/codegen.rs

+3-4
Original file line numberDiff line numberDiff line change
@@ -523,10 +523,10 @@ pub fn generate_code(sm: &ParsedStateMachine) -> proc_macro2::TokenStream {
523523
quote! {}
524524
};
525525

526-
let (is_async, is_async_trait) = if is_async_state_machine {
527-
(quote! { async }, quote! { #[smlang::async_trait] })
526+
let is_async = if is_async_state_machine {
527+
quote! { async }
528528
} else {
529-
(quote! {}, quote! {})
529+
quote! {}
530530
};
531531

532532
let error_type = if sm.custom_guard_error {
@@ -543,7 +543,6 @@ pub fn generate_code(sm: &ParsedStateMachine) -> proc_macro2::TokenStream {
543543
quote! {
544544
/// This trait outlines the guards and actions that need to be implemented for the state
545545
/// machine.
546-
#is_async_trait
547546
pub trait #state_machine_context_type_name {
548547
#guard_error
549548
#guard_list

src/lib.rs

+52-38
Original file line numberDiff line numberDiff line change
@@ -10,61 +10,75 @@
1010
//! **documentation of the project**, this comes from the procedural macro also generating
1111
//! documentation.
1212
//!
13-
//! # DSL
13+
#![doc = include_str!("../docs/dsl.md")]
1414
//!
15-
//! Please consult the README for the DSL specification.
15+
//! # Example
1616
//!
17-
//! # Examples
17+
//! Below is an example of the state machine macro in use along with the code that would be
18+
//! generated for this sample to demonstrate how this library is used.
1819
//!
19-
//! Please consult the README for examples.
20+
//! ```rust
21+
//! use smlang::statemachine;
2022
//!
21-
//! # Errors
22-
//!
23-
//! `StateMachine::process_event` will return `Ok(NextState)` if the transition was successful,
24-
//! or `Err(Error::GuardFailed)` if the guard failed, or `Err(Error::InvalidEvent)` if an event
25-
//! which should not come at this stage of the state machine was processed.
26-
//!
27-
//! # Panics
28-
//!
29-
//! There are no `panic!` in this library.
30-
//!
31-
//! # Unsafe
32-
//!
33-
//! There is no use of `unsafe` in this library.
34-
//!
35-
//! # Auto-generated types
36-
//!
37-
//! ```ignore
38-
//! // Auto generated enum of states
39-
//! enum States { ... }
23+
//! statemachine! {
24+
//! name: Sample,
25+
//! derive_states: [Debug],
26+
//! derive_events: [Clone, Debug],
27+
//! transitions: {
28+
//! *Init + InitEvent [ guard_init ] / action_init = Ready,
29+
//! }
30+
//! }
4031
//! ```
4132
//!
33+
//! Results in the following code:
4234
//! ```ignore
43-
//! // Auto generated enum of possible events
44-
//! enum Events { ... }
45-
//! ```
35+
//! #[derive(Debug)]
36+
//! enum SampleStates {
37+
//! Init,
38+
//! Ready,
39+
//! }
4640
//!
47-
//! ```ignore
48-
//! // Auto generated struct which holds the state machine implementation
49-
//! struct StateMachine { ... }
50-
//! ```
41+
//! #[derive(Clone, Debug)]
42+
//! enum SampleEvents {
43+
//! InitEvent,
44+
//! }
5145
//!
52-
//! # State machine generated API
46+
//! struct SampleStateMachine<C: SampleStateMachineContext> {
47+
//! // ...
48+
//! }
5349
//!
54-
//! ```ignore
55-
//! struct StateMachine {
50+
//! enum SampleError {
51+
//! InvalidEvent,
52+
//! GuardFailed,
53+
//! // ...
54+
//! }
55+
//!
56+
//! impl<C: SampleStateMachineContext> SampleStateMachine<C> {
5657
//! /// Creates a state machine with the starting state
57-
//! pub fn new() -> Self;
58+
//! pub fn new() -> Self { /**/ }
5859
//!
5960
//! /// Returns the current state
60-
//! pub fn state(&self) -> States;
61+
//! pub fn state(&self) -> States { /**/ }
6162
//!
6263
//! /// Process an event
63-
//! pub fn process_event(&mut self, event: Events) -> Result<States, Error>;
64+
//! ///
65+
//! /// # Returns
66+
//! /// `Ok(NextState)` if the transition was successful or `Err()` if the transition failed.
67+
//! /// guard failed
68+
//! pub fn process_event(&mut self, event: Events) -> Result<SampleStates, SampleError> {
69+
//! # Err(SampleError::InvalidEvent);
70+
//! /**/
71+
//! }
72+
//! }
73+
//!
74+
//! trait SampleStateMachineContext {
75+
//! // Called to guard the transition to `Ready`. Returns an `Err` if the guard fails.
76+
//! fn guard_init(&mut self) -> Result<(), ()>;
77+
//!
78+
//! // Called when transitioning to `Ready`.
79+
//! fn action_init(&mut self);
6480
//! }
6581
//! ```
66-
6782
#![no_std]
6883

69-
pub use async_trait::async_trait;
7084
pub use smlang_macros::statemachine;

0 commit comments

Comments
 (0)