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
use crate::core::StateMachine;
use std::collections::HashMap;
/// State machine documentation generator
///
/// Provides functionality to generate Mermaid diagrams and transition tables.
pub struct StateMachineDoc<SM: StateMachine> {
_phantom: std::marker::PhantomData<SM>,
}
impl<SM: StateMachine> StateMachineDoc<SM> {
/// Check if an input should be included in documentation
///
/// Inputs starting with underscore are typically used for internal debugging
/// or special purposes and should not be included in user documentation.
fn should_include_input(input: &SM::Input) -> bool {
!SM::input_name(input).starts_with('_')
}
/// Generate Mermaid state diagram
///
/// Generates a state diagram definition compliant with Mermaid syntax,
/// which can be used to visualize the state machine structure.
/// Self-loops and normal transitions are handled separately for better readability.
///
/// # Returns
/// Returns a Mermaid-formatted state diagram string
pub fn generate_mermaid() -> String {
let mut mermaid = String::from("stateDiagram-v2\n");
// Add initial state marker
let initial = SM::initial_state();
mermaid.push_str(&format!(" [*] --> {}\n", SM::state_name(&initial)));
// Collect normal transitions and self-loops separately
let mut normal_transitions = HashMap::new();
let mut self_loops = HashMap::new();
for state in SM::states() {
for input in SM::valid_inputs(&state) {
// Skip inputs starting with underscore
if !Self::should_include_input(&input) {
continue;
}
if let Some(next_state) = SM::next_state(&state, &input) {
if state == next_state {
// Self-loop
self_loops
.entry(state.clone())
.or_insert_with(Vec::new)
.push(input.clone());
} else {
// Normal transition
let key = (state.clone(), next_state.clone());
normal_transitions
.entry(key)
.or_insert_with(Vec::new)
.push(input.clone());
}
}
}
}
// Add normal transitions
for ((from, to), inputs) in normal_transitions {
let input_labels: Vec<String> = inputs.iter().map(|i| SM::input_name(i)).collect();
let label = input_labels.join(" / ");
mermaid.push_str(&format!(
" {} --> {} : {}\n",
SM::state_name(&from),
SM::state_name(&to),
label
));
}
// Add self-loops with different formats based on input count
for (state, inputs) in self_loops {
if inputs.len() <= 2 {
// Merge few inputs for display
let input_labels: Vec<String> = inputs.iter().map(|i| SM::input_name(i)).collect();
let label = input_labels.join(" / ");
mermaid.push_str(&format!(
" {} --> {} : {}\n",
SM::state_name(&state),
SM::state_name(&state),
label
));
} else {
// Display many inputs separately to avoid cluttered diagrams
for input in inputs {
mermaid.push_str(&format!(
" {} --> {} : {}\n",
SM::state_name(&state),
SM::state_name(&state),
SM::input_name(&input)
));
}
}
}
mermaid
}
/// Generate state transition table
///
/// Generates a Markdown-formatted state transition table listing all valid state transitions.
///
/// # Returns
/// Returns a Markdown-formatted transition table string
pub fn generate_transition_table() -> String {
let mut table = String::from("# State Transition Table\n\n");
table.push_str("| Current State | Input | Next State |\n");
table.push_str("|---------------|-------|------------|\n");
for state in SM::states() {
for input in SM::valid_inputs(&state) {
// Skip inputs starting with underscore
if !Self::should_include_input(&input) {
continue;
}
if let Some(next_state) = SM::next_state(&state, &input) {
table.push_str(&format!(
"| {} | {} | {} |\n",
SM::state_name(&state),
SM::input_name(&input),
SM::state_name(&next_state)
));
}
}
}
table
}
/// Generate state machine statistics
///
/// Generates a report containing statistics such as state count, transition count, etc.
///
/// # Returns
/// Returns a statistics information string
pub fn generate_statistics() -> String {
let states = SM::states();
let inputs = SM::inputs();
let mut transition_count = 0;
let mut self_loop_count = 0;
for state in &states {
for input in SM::valid_inputs(state) {
if let Some(next_state) = SM::next_state(state, &input) {
if *state == next_state {
self_loop_count += 1;
} else {
transition_count += 1;
}
}
}
}
format!(
"# State Machine Statistics\n\n\
- **Number of States**: {}\n\
- **Number of Input Types**: {}\n\
- **Number of Transitions**: {}\n\
- **Number of Self-loops**: {}\n\
- **Total Transitions**: {}\n\
- **Initial State**: {}\n",
states.len(),
inputs.len(),
transition_count,
self_loop_count,
transition_count + self_loop_count,
SM::state_name(&SM::initial_state())
)
}
/// Generate complete documentation
///
/// Complete documentation containing statistics, transition tables, and Mermaid diagrams.
///
/// # Returns
/// Returns the complete documentation string
pub fn generate_full_documentation() -> String {
let mut doc = String::new();
// Add title
doc.push_str("# State Machine Documentation\n\n");
// Add statistics
doc.push_str(&Self::generate_statistics());
doc.push('\n');
// Add transition table
doc.push_str(&Self::generate_transition_table());
doc.push('\n');
// Add Mermaid diagram
doc.push_str("# State Diagram\n\n");
doc.push_str("```mermaid\n");
doc.push_str(&Self::generate_mermaid());
doc.push_str("```\n");
doc
}
}