1#![cfg_attr(test, allow(dead_code, unreachable_code))]
84
85pub extern crate log;
86extern crate marksman_escape;
87extern crate variant_count;
88
89pub use variant_count::VariantCount;
90
91mod macro_def;
92
93pub trait MachineDotfile {
97 fn name() -> &'static str;
99 fn type_vars() -> Vec <String>;
100 fn extended_state_names() -> Vec <&'static str>;
101 fn extended_state_types() -> Vec <&'static str>;
102 fn extended_state_defaults() -> Vec <&'static str>;
103 fn self_reference() -> &'static str;
104 fn states() -> Vec <&'static str>;
105 fn state_data_names() -> Vec <Vec <&'static str>>;
106 fn state_data_types() -> Vec <Vec <&'static str>>;
107 fn state_data_defaults() -> Vec <Vec <&'static str>>;
108 fn state_data_pretty_defaults() -> Vec <Vec <String>>;
109 fn state_initial() -> &'static str;
110 fn state_terminal() -> &'static str;
111 fn events() -> Vec <&'static str>;
112 fn event_sources() -> Vec <&'static str>;
113 fn event_targets() -> Vec <&'static str>;
114 fn event_actions() -> Vec <&'static str>;
115 fn dotfile() -> String where Self : Sized {
119 machine_dotfile::<Self> (true, false, false)
120 }
121 fn dotfile_show_defaults() -> String where Self : Sized {
124 machine_dotfile::<Self> (false, false, false)
125 }
126 fn dotfile_pretty_defaults() -> String where Self : Sized {
132 machine_dotfile::<Self> (false, true, false)
133 }
134 fn dotfile_hide_actions() -> String where Self : Sized {
136 machine_dotfile::<Self> (true, false, true)
137 }
138}
139
140#[derive(Debug, Eq, PartialEq)]
144pub enum HandleEventException {
145 WrongState
146}
147
148fn machine_dotfile <M : MachineDotfile>
154 (hide_defaults : bool, pretty_defaults : bool, hide_actions : bool) -> String
155{
156 let mut s = String::new();
157 s.push_str (
162 "digraph {\n \
163 overlap=scale\n \
164 rankdir=LR\n \
165 node [shape=record, style=rounded, fontname=\"Sans Bold\"]\n \
166 edge [fontname=\"Sans\"]\n");
167
168 { s.push_str (format!(
172 " subgraph cluster_{} {{\n", M::name()).as_str());
173 let title_string = {
174 let mut s = String::new();
175 s.push_str (M::name());
176 if !M::type_vars().is_empty() {
177 s.push ('<');
178 let type_vars = M::type_vars();
179 for string in type_vars {
180 s.push_str (string.as_str());
181 s.push (',');
182 }
183 assert_eq!(s.pop(), Some (','));
184 s.push ('>');
185 }
186 s
187 };
188 s.push_str (format!(" label=<{}", escape (title_string)).as_str());
189
190 let mut mono_font = false;
192 let extended_state_names = M::extended_state_names();
193 let extended_state_types = M::extended_state_types();
194 let extended_state_defaults = M::extended_state_defaults();
195 debug_assert_eq!(extended_state_names.len(), extended_state_types.len());
196 debug_assert_eq!(extended_state_types.len(), extended_state_defaults.len());
197
198 if !extended_state_names.is_empty() {
199 s.push_str ("<FONT FACE=\"Mono\"><BR/><BR/>\n");
200 mono_font = true;
201 debug_assert!(mono_font);
205
206 let mut extended_string = String::new();
207 let separator = ",<BR ALIGN=\"LEFT\"/>\n";
208
209 let longest_fieldname = extended_state_names.iter()
210 .fold (0, |longest, fieldname| std::cmp::max (longest, fieldname.len()));
211
212 let longest_typename = extended_state_types.iter()
213 .fold (0, |longest, typename| std::cmp::max (longest, typename.len()));
214
215 for (i,f) in extended_state_names.iter().enumerate() {
216 let spacer1 : String =
217 std::iter::repeat_n (' ', longest_fieldname - f.len()).collect();
218 let spacer2 : String =
219 std::iter::repeat_n (' ', longest_typename - extended_state_types[i].len())
220 .collect();
221
222 if !hide_defaults && !extended_state_defaults[i].is_empty() {
223 extended_string.push_str (escape (format!(
224 "{}{} : {}{} = {}",
225 f, spacer1, extended_state_types[i], spacer2, extended_state_defaults[i]
226 )).as_str());
227 } else {
228 extended_string.push_str (escape (format!(
229 "{}{} : {}", f, spacer1, extended_state_types[i]
230 )).as_str());
231 }
232 extended_string.push_str (separator.to_string().as_str());
233 }
234
235 let len = extended_string.len();
236 extended_string.truncate (len - separator.len());
237 s.push_str (extended_string.as_str());
238 } s.push_str ("<BR ALIGN=\"LEFT\"/>");
241 let self_reference = M::self_reference();
242 if !self_reference.is_empty() && mono_font {
243 s.push_str (format!("@ {self_reference}<BR ALIGN=\"CENTER\"/>").as_str());
244 }
245 if !extended_state_names.is_empty() {
246 s.push_str ("\n ");
247 }
248
249 if mono_font {
252 s.push_str ("</FONT><BR/>");
253 }
254 s.push_str (">\
255 \n shape=record\
256 \n style=rounded\
257 \n fontname=\"Sans Bold Italic\"\n");
258 } s.push_str (
265 " INITIAL [label=\"\", shape=circle, width=0.2, \
266 style=filled, fillcolor=black]\n");
267 let state_data_names = M::state_data_names();
269 let state_data_types = M::state_data_types();
270 let state_data_defaults : Vec <Vec <String>> = if !pretty_defaults {
271 M::state_data_defaults().into_iter().map (
272 |v| v.into_iter().map (str::to_string).collect()
273 ).collect()
274 } else {
275 let pretty_defaults = M::state_data_pretty_defaults();
276 pretty_defaults.into_iter().map (
277 |v| v.into_iter().map (|pretty_newline| {
278 let mut pretty_br = String::new();
279 let separator = "<BR ALIGN=\"LEFT\"/>\n";
280 for line in pretty_newline.lines() {
281 pretty_br.push_str (escape (line.to_string()).as_str());
282 pretty_br.push_str (separator);
283 }
284 let len = pretty_br.len();
285 pretty_br.truncate (len - separator.len());
286 pretty_br
287 }).collect()
288 ).collect()
289 };
290 debug_assert_eq!(state_data_names.len(), state_data_types.len());
291 debug_assert_eq!(state_data_types.len(), state_data_defaults.len());
292
293 for (i, state) in M::states().iter().enumerate() {
295 let mut mono_font = false;
296 let state_data_names = &state_data_names[i];
297 let state_data_types = &state_data_types[i];
298 let state_data_defaults = &state_data_defaults[i];
299 debug_assert_eq!(state_data_names.len(), state_data_types.len());
300 debug_assert_eq!(state_data_types.len(), state_data_defaults.len());
301 s.push_str (format!(" {state} [label=<<B>{state}</B>").as_str());
302 if !state_data_names.is_empty() {
305 if !mono_font {
306 s.push_str ("|<FONT FACE=\"Mono\"><BR/>\n");
307 mono_font = true;
308 }
309 let mut data_string = String::new();
310 let separator = ",<BR ALIGN=\"LEFT\"/>\n";
311 let longest_fieldname = state_data_names.iter()
312 .fold (0, |longest, fieldname| std::cmp::max (longest, fieldname.len()));
313 let longest_typename = state_data_types.iter()
314 .fold (0, |longest, typename| std::cmp::max (longest, typename.len()));
315 for (i,f) in state_data_names.iter().enumerate() {
316 let spacer1 : String =
317 std::iter::repeat_n (' ', longest_fieldname - f.len()).collect();
318 let spacer2 : String =
319 std::iter::repeat_n (' ', longest_typename - state_data_types[i].len())
320 .collect();
321 if !hide_defaults && !state_data_defaults[i].is_empty() {
322 data_string.push_str (escape (format!(
323 "{}{} : {}{} = {}",
324 f, spacer1, state_data_types[i], spacer2, state_data_defaults[i]
325 )).as_str());
326 } else {
327 data_string.push_str (escape (format!(
328 "{}{} : {}", f, spacer1, state_data_types[i]
329 )).as_str());
330 }
331 data_string.push_str (separator.to_string().as_str());
332 }
333 let len = data_string.len();
334 data_string.truncate (len - separator.len());
335 s.push_str (data_string.as_str());
336 }
337
338 if mono_font {
351 s.push_str ("<BR ALIGN=\"LEFT\"/></FONT>");
352 }
353 s.push_str (">]\n");
354 } s.push_str (format!(
363 " INITIAL -> {}\n", M::state_initial()).as_str());
364 let event_sources = M::event_sources();
365 let event_targets = M::event_targets();
366 let event_actions = M::event_actions();
367 let mut universal = false;
368 for (i, event) in M::events().into_iter().enumerate() {
370 let source = event_sources[i];
371 let mut target = event_targets[i];
372 let action = event_actions[i];
373 if target.is_empty() { target = source;
375 }
376
377 if source == "*" {
378 universal = true;
379 }
380 s.push_str (format!(
381 " \"{source}\" -> \"{target}\" [label=<<FONT FACE=\"Sans Italic\">{event}</FONT>"
382 ).as_str());
383
384 let mut mono_font = false;
385 if !hide_actions && !action.is_empty() {
391 match action {
392 "{}" | "{ }" => {}
394 _ => {
395 if !mono_font {
396 s.push_str ("<FONT FACE=\"Mono\"><BR/>");
397 mono_font = true;
398 }
399 let action_string = {
401 let mut s : String = action.split_whitespace().map (
402 |s| {
403 let mut s = s.to_string();
404 s.push (' ');
405 s
406 }
407 ).collect();
408 assert_eq!(s.pop(), Some (' '));
409 s
410 };
411 s.push_str (escape (action_string).as_str());
414 }
415 }
416 }
417
418 if mono_font {
419 s.push_str ("</FONT>");
420 }
421 s.push_str (">]\n");
422 } if universal {
425 for state in M::states() {
426 s.push_str (format!(
427 " {state} -> \"*\" [style=dashed, color=gray]").as_str());
428 }
429 }
430
431 let state_terminal = M::state_terminal();
434 if !state_terminal.is_empty() {
435 s.push_str (
436 " TERMINAL [label=\"\", shape=doublecircle, width=0.2,\
437 \n style=filled, fillcolor=black]\n");
438 s.push_str (format!(
439 " {state_terminal} -> TERMINAL\n").as_str());
440 }
441 s.push_str (
447 " }\n\
448 }");
449 s
450} #[inline]
454fn escape (s : String) -> String {
455 use marksman_escape::Escape;
456 String::from_utf8 (Escape::new (s.bytes()).collect()).unwrap()
457}
458
459#[cfg(doc)]
460pub mod example {
461 use crate::def_machine_debug;
463 def_machine_debug! {
464 Door (open_count : u64) @ door {
465 STATES [
466 state Closed (knock_count : u64) {
467 exit { println!("final knock count: {}", knock_count); }
468 }
469 state Opened () {
470 entry { println!("open count: {}", open_count); }
471 }
472 ]
473 EVENTS [
474 event Knock <Closed> () { knock_count } => { *knock_count += 1; }
475 event Open <Closed> => <Opened> () {} => { *open_count += 1; }
476 event Close <Opened> => <Closed> ()
477 ]
478 initial_state: Closed {
479 initial_action: { println!("hello"); }
480 }
481 terminal_state: Closed {
482 terminate_success: { println!("goodbye") }
483 terminate_failure: {
484 panic!("door was left: {:?}", door.state())
485 }
486 }
487 }
488 }
489}
490
491#[cfg(test)]
492mod tests {
493 use super::*;
494 #[test]
495 fn initial() {
496 {
497 def_machine!{
498 Test () {
499 STATES [ state A () ]
500 EVENTS [ ]
501 initial_state: A
502 }
503 }
504 let test = Test::initial();
505 assert_eq!(test.state_id(), StateId::A);
506 } {
507 def_machine_debug!{
508 Test () {
509 STATES [ state A () ]
510 EVENTS [ ]
511 initial_state: A
512 }
513 }
514 let test = Test::initial();
515 assert_eq!(test.state_id(), StateId::A);
516 }
517 }
518 #[test]
519 fn new() {
520 {
521 def_machine!{
522 Test () {
523 STATES [ state A () ]
524 EVENTS [ ]
525 initial_state: A
526 }
527 }
528 let test = Test::new (ExtendedState::new());
529 assert_eq!(test.state_id(), StateId::A);
530 } {
531 def_machine_debug!{
532 Test () {
533 STATES [ state A () ]
534 EVENTS [ ]
535 initial_state: A
536 }
537 }
538 let test = Test::new (ExtendedState::new());
539 assert_eq!(test.state_id(), StateId::A);
540 } {
541 def_machine_nodefault!{
542 Test () {
543 STATES [ state A () ]
544 EVENTS [ ]
545 initial_state: A
546 }
547 }
548 let test = Test::new (ExtendedState::new().unwrap());
549 assert_eq!(test.state_id(), StateId::A);
550 } {
551 def_machine_nodefault_debug!{
552 Test () {
553 STATES [ state A () ]
554 EVENTS [ ]
555 initial_state: A
556 }
557 }
558 let test = Test::new (ExtendedState::new().unwrap());
559 assert_eq!(test.state_id(), StateId::A);
560 }
561 }
562 #[test]
563 fn event_internal() {
564 {
565 def_machine!{
566 Test () {
567 STATES [ state A () ]
568 EVENTS [ event E <A> () ]
569 initial_state: A
570 }
571 }
572 let mut test = Test::initial();
573 test.handle_event (EventId::E.into()).unwrap();
574 } {
575 def_machine_debug!{
576 Test () {
577 STATES [ state A () ]
578 EVENTS [ event E <A> () ]
579 initial_state: A
580 }
581 }
582 let mut test = Test::initial();
583 test.handle_event (EventId::E.into()).unwrap();
584 } {
585 def_machine_nodefault!{
586 Test () {
587 STATES [ state A () ]
588 EVENTS [ event E <A> () ]
589 initial_state: A
590 }
591 }
592 let mut test = Test::new (ExtendedState::new().unwrap());
593 test.handle_event (EventParams::E{}.into()).unwrap();
594 } {
595 def_machine_nodefault_debug!{
596 Test () {
597 STATES [ state A () ]
598 EVENTS [ event E <A> () ]
599 initial_state: A
600 }
601 }
602 let mut test = Test::new (ExtendedState::new().unwrap());
603 test.handle_event (EventParams::E{}.into()).unwrap();
604 }
605 }
606 #[test]
607 fn event_external() {
608 {
609 def_machine!{
610 Test () {
611 STATES [
612 state A ()
613 state B ()
614 ]
615 EVENTS [ event E <A> => <B> () ]
616 initial_state: A
617 }
618 }
619 let mut test = Test::initial();
620 test.handle_event (EventId::E.into()).unwrap();
621 assert_eq!(test.state_id(), StateId::B);
622 } {
623 def_machine_debug!{
624 Test () {
625 STATES [
626 state A ()
627 state B ()
628 ]
629 EVENTS [ event E <A> => <B> () ]
630 initial_state: A
631 }
632 }
633 let mut test = Test::initial();
634 test.handle_event (EventId::E.into()).unwrap();
635 assert_eq!(test.state_id(), StateId::B);
636 } {
637 def_machine_nodefault!{
638 Test () {
639 STATES [
640 state A ()
641 state B ()
642 ]
643 EVENTS [ event E <A> => <B> () ]
644 initial_state: A
645 }
646 }
647 let mut test = Test::new (ExtendedState::new().unwrap());
648 test.handle_event (EventParams::E{}.into()).unwrap();
649 assert_eq!(test.state_id(), StateId::B);
650 } {
651 def_machine_nodefault_debug!{
652 Test () {
653 STATES [
654 state A ()
655 state B ()
656 ]
657 EVENTS [ event E <A> => <B> () ]
658 initial_state: A
659 }
660 }
661 let mut test = Test::new (ExtendedState::new().unwrap());
662 test.handle_event (EventParams::E{}.into()).unwrap();
663 assert_eq!(test.state_id(), StateId::B);
664 }
665 }
666}