Skip to content

Commit

Permalink
Merge pull request #88 from prabhpreet/custom-states-events-attr
Browse files Browse the repository at this point in the history
Add custom attributes for States and Events enum
  • Loading branch information
ryan-summers authored Aug 19, 2024
2 parents f9a12bd + 3986251 commit e1ba392
Show file tree
Hide file tree
Showing 12 changed files with 85 additions and 69 deletions.
8 changes: 7 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,13 @@ and this project adheres to [Semantic Versioning](http://semver.org/).

## [Unreleased]

* None yet!
### Added

- Add support for defining States and Events attributes using `states_attr` and `events_attr` fields

### Changed

- [breaking] Remove `derive_states` and `derive_events` fields in lieu of `states_attr` and `events_attr` to define attributes generically

## [v0.8.0] - 2024-08-07

Expand Down
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ smlang-macros = { path = "macros", version = "0.8.0" }
[dev-dependencies]
smol = "1"
derive_more = "0.99.17"
serde = {version = "1",features = ["derive"]}

[target.'cfg(not(target_os = "none"))'.dev-dependencies]
trybuild = "1.0"
Expand Down
10 changes: 5 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -370,18 +370,18 @@ in `dominos`.

## Helpers

### Auto-derive certain traits for states and events
### Specify attributes for states and events

Setting `derive_events` and `derive_states` fields to an array of traits adds a derive expression to `Events` and `States` enums respectively. To derive Display, use `derive_more::Display`.
Setting `events_attr` and `states_attr` fields to a list of attributes to `Events` and `States` enums respectively. To derive Display, use `derive_more::Display`.


```rust
use core::Debug;
use derive_more::Display;
// ...
statemachine!{
derive_states: [Debug, Display],
derive_events: [Debug, Display],
states_attr: #[derive(Debug, Display)],
events_attr: #[derive(Debug, Display)],
transitions: {
*State1 + Event1 = State2,
}
Expand Down Expand Up @@ -409,7 +409,7 @@ fn log_action(&self, action: &'static str) {}
fn log_state_change(&self, new_state: &States) {}
```

See `examples/state_machine_logger.rs` for an example which uses `derive_states` and `derive_events` to derive `Debug` implementations for easy logging.
See `examples/state_machine_logger.rs` for an example which uses `states_attr` and `events_attr` to derive `Debug` implementations for easy logging.

## Contributors

Expand Down
10 changes: 5 additions & 5 deletions docs/dsl.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,11 @@ statemachine!{
// error type instead of `()`.
custom_error: false,

// [Optional] A list of derive names for the generated `States` and `Events`
// enumerations respectively. For example, to `#[derive(Debug)]`, these
// would both be specified as `[Debug]`.
derive_states: [],
derive_events: [],
// [Optional] A list of attributes for the generated `States` and `Events`
// enumerations respectively. For example, to `#[derive(Debug)]` and `#[repr(u8)], these
// would both be specified in a list as follows:
states_attr: #[derive(Debug)] #[repr(u8)],
events_attr: #[derive(Debug)] #[repr(u8)],

transitions: {
// * denotes the starting state
Expand Down
4 changes: 2 additions & 2 deletions examples/dominos.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
use smlang::statemachine;

statemachine! {
derive_states: [Debug],
derive_events: [Debug],
states_attr: #[derive(Debug)],
events_attr: #[derive(Debug)],
transitions: {
*D0 + ToD1 / to_d2 = D1,
D1(Option<Events>) + ToD2 / to_d3 = D2,
Expand Down
2 changes: 1 addition & 1 deletion examples/named_ex1.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use smlang::statemachine;

statemachine! {
name: Linear,
derive_states: [Debug],
states_attr: #[derive(Debug)],
transitions: {
*State1 + Event1 = State2,
State2 + Event2 = State3,
Expand Down
4 changes: 2 additions & 2 deletions examples/state_machine_logger.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ pub struct MyEventData(pub u32);
pub struct MyStateData(pub u32);

statemachine! {
derive_states: [Debug],
derive_events: [Debug],
states_attr: #[derive(Debug)],
events_attr: #[derive(Debug)],
transitions: {
*State1 + Event1(MyEventData) [guard1] / action1 = State2,
State2(MyStateData) + Event2 [guard2] / action2 = State3,
Expand Down
8 changes: 4 additions & 4 deletions macros/src/codegen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -539,8 +539,8 @@ pub fn generate_code(sm: &ParsedStateMachine) -> proc_macro2::TokenStream {
quote! {#error_type_name}
};

let derive_states_list = &sm.derive_states;
let derive_events_list = &sm.derive_events;
let states_attr_list = &sm.states_attr;
let events_attr_list = &sm.events_attr;
// Build the states and events output
quote! {
/// This trait outlines the guards and actions that need to be implemented for the state
Expand Down Expand Up @@ -575,7 +575,7 @@ pub fn generate_code(sm: &ParsedStateMachine) -> proc_macro2::TokenStream {

/// List of auto-generated states.
#[allow(missing_docs)]
#[derive(#(#derive_states_list),*)]
#(#states_attr_list)*
pub enum #states_type_name <#state_lifetimes> { #(#state_list),* }

/// Manually define PartialEq for #states_type_name based on variant only to address issue-#21
Expand All @@ -588,7 +588,7 @@ pub fn generate_code(sm: &ParsedStateMachine) -> proc_macro2::TokenStream {

/// List of auto-generated events.
#[allow(missing_docs)]
#[derive(#(#derive_events_list),*)]
#(#events_attr_list)*
pub enum #events_type_name <#event_lifetimes> { #(#event_list),* }

/// Manually define PartialEq for #events_type_name based on variant only to address issue-#21
Expand Down
10 changes: 5 additions & 5 deletions macros/src/parser/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ use proc_macro2::{Span, TokenStream};
use crate::parser::event::Transition;
use std::collections::{hash_map, HashMap};
use std::fmt;
use syn::{parse, Ident, Type};
use syn::{parse, Attribute, Ident, Type};
use transition::StateTransition;
pub type TransitionMap = HashMap<String, HashMap<String, EventMapping>>;

Expand Down Expand Up @@ -46,8 +46,8 @@ impl fmt::Display for AsyncIdent {
#[derive(Debug)]
pub struct ParsedStateMachine {
pub name: Option<Ident>,
pub derive_states: Vec<Ident>,
pub derive_events: Vec<Ident>,
pub states_attr: Vec<Attribute>,
pub events_attr: Vec<Attribute>,
pub temporary_context_type: Option<Type>,
pub custom_error: bool,
pub states: HashMap<String, Ident>,
Expand Down Expand Up @@ -242,8 +242,8 @@ impl ParsedStateMachine {

Ok(ParsedStateMachine {
name: sm.name,
derive_states: sm.derive_states,
derive_events: sm.derive_events,
states_attr: sm.states_attr,
events_attr: sm.events_attr,
temporary_context_type: sm.temporary_context_type,
custom_error: sm.custom_error,
states,
Expand Down
49 changes: 14 additions & 35 deletions macros/src/parser/state_machine.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
use super::transition::{StateTransition, StateTransitions};
use syn::{braced, bracketed, parse, spanned::Spanned, token, Ident, Token, Type};
use syn::{braced, parse, spanned::Spanned, token, Attribute, Ident, Token, Type};

#[derive(Debug)]
pub struct StateMachine {
pub temporary_context_type: Option<Type>,
pub custom_error: bool,
pub transitions: Vec<StateTransition>,
pub name: Option<Ident>,
pub derive_states: Vec<Ident>,
pub derive_events: Vec<Ident>,
pub states_attr: Vec<Attribute>,
pub events_attr: Vec<Attribute>,
}

impl StateMachine {
Expand All @@ -18,8 +18,8 @@ impl StateMachine {
custom_error: false,
transitions: Vec::new(),
name: None,
derive_states: Vec::new(),
derive_events: Vec::new(),
states_attr: Vec::new(),
events_attr: Vec::new(),
}
}

Expand Down Expand Up @@ -106,38 +106,17 @@ impl parse::Parse for StateMachine {
input.parse::<Token![:]>()?;
statemachine.name = Some(input.parse::<Ident>()?);
}
"derive_states" => {

"states_attr" => {
input.parse::<Token![:]>()?;
if input.peek(token::Bracket) {
let content;
bracketed!(content in input);
loop {
if content.is_empty() {
break;
};
let trait_ = content.parse::<Ident>()?;
statemachine.derive_states.push(trait_);
if content.parse::<Token![,]>().is_err() {
break;
};
}
}
statemachine.states_attr = Attribute::parse_outer(input)?;
}
"derive_events" => {

"events_attr" => {
input.parse::<Token![:]>()?;
let content;
bracketed!(content in input);
loop {
if content.is_empty() {
break;
};
let trait_ = content.parse::<Ident>()?;
statemachine.derive_events.push(trait_);
if content.parse::<Token![,]>().is_err() {
break;
};
}
statemachine.events_attr = Attribute::parse_outer(input)?;
}

keyword => {
return Err(parse::Error::new(
input.span(),
Expand All @@ -146,8 +125,8 @@ impl parse::Parse for StateMachine {
\"transitions\", \
\"temporary_context\", \
\"custom_error\", \
\"derive_states\", \
\"derive_events\"
\"states_attr\", \
\"events_attr\"
]",
keyword
),
Expand Down
4 changes: 2 additions & 2 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@
//!
//! statemachine! {
//! name: Sample,
//! derive_states: [Debug],
//! derive_events: [Clone, Debug],
//! states_attr: #[derive(Debug)],
//! events_attr: #[derive(Clone, Debug)],
//! transitions: {
//! *Init + InitEvent [ guard_init ] / action_init = Ready,
//! }
Expand Down
44 changes: 37 additions & 7 deletions tests/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -121,8 +121,8 @@ fn multiple_lifetimes() {
#[test]
fn derive_display_events_states() {
statemachine! {
derive_events: [Debug,Display],
derive_states: [Debug,Display],
events_attr: #[derive(Debug,Display)],
states_attr: #[derive(Debug,Display)],
transitions: {
*Init + Event = End,
}
Expand All @@ -145,8 +145,8 @@ fn derive_display_events_states() {
fn named_derive_display_events_states() {
statemachine! {
name: SM,
derive_events: [Debug,Display],
derive_states: [Debug,Display],
events_attr: #[derive(Debug,Display)],
states_attr: #[derive(Debug,Display)],
transitions: {
*Init + Event = End,
}
Expand Down Expand Up @@ -205,7 +205,7 @@ fn guard_expressions() {
pub struct Entry(pub u32);

statemachine! {
derive_states: [Display, Debug],
states_attr: #[derive(Display, Debug)],
transitions: {
*Init + Login(&'a Entry) [valid_entry] / attempt = LoggedIn,
Init + Login(&'a Entry) [!valid_entry && !too_many_attempts] / attempt = Init,
Expand Down Expand Up @@ -380,7 +380,7 @@ fn test_internal_transition_with_data() {
// State4(State3Data) + Event3 / action_3 = State4(State3Data),
_ + Event3 / action_3 = _,
},
derive_states: [Debug, Clone, Copy, Eq ]
states_attr: #[derive(Debug, Clone, Copy, Eq)]
}
/// Context
#[derive(Default, Debug, PartialEq, Eq)]
Expand Down Expand Up @@ -463,7 +463,7 @@ fn test_wildcard_states_and_internal_transitions() {
_ + Event1 / increment_count, // Internal transition (implicit: omitting target state)
_ + Event3 / increment_count = _ , // Internal transition (explicit: using _ as target state)
},
derive_states: [Debug, Clone, Copy]
states_attr: #[derive(Debug, Clone, Copy)]
}
#[derive(Debug)]
pub struct Context {
Expand All @@ -487,3 +487,33 @@ fn test_wildcard_states_and_internal_transitions() {
assert!(sm.process_event(Events::Event2).is_err()); // InvalidEvent
assert_eq!(States::State3, sm.state);
}
#[test]
fn test_specify_attrs() {
#![deny(non_camel_case_types)]
use serde::Serialize;
statemachine! {
transitions: {
*State1 + tostate2 = State2,
State2 + tostate3 / increment_count = State3
},
states_attr: #[derive(Debug, Clone, Copy, Serialize)] #[non_exhaustive] #[repr(u8)] #[serde(tag="type")],
events_attr: #[derive(Debug)] #[allow(non_camel_case_types)]
}

#[derive(Debug, Default)]
pub struct Context {
count: u32,
}

impl StateMachineContext for Context {
fn increment_count(&mut self) -> Result<(), ()> {
self.count += 1;
Ok(())
}
}
let mut sm = StateMachine::new(Context::default());

assert_eq!(sm.state().clone(), States::State1);
assert_transition!(sm, Events::tostate2, States::State2, 0);
assert_transition!(sm, Events::tostate3, States::State3, 1);
}

0 comments on commit e1ba392

Please sign in to comment.