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, 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_str ("<");
178 let type_vars = M::type_vars();
179 for string in type_vars {
180 s.push_str (string.as_str());
181 s.push_str (",");
182 }
183 assert_eq!(s.pop(), Some (','));
184 s.push_str (">");
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().fold (
210 0, |longest, ref fieldname| std::cmp::max (longest, fieldname.len())
211 );
212
213 let longest_typename = extended_state_types.iter().fold (
214 0, |longest, ref typename| std::cmp::max (longest, typename.len())
215 );
216
217 for (i,f) in extended_state_names.iter().enumerate() {
218 let spacer1 : String = std::iter::repeat (' ')
219 .take (longest_fieldname - f.len())
220 .collect();
221 let spacer2 : String = std::iter::repeat (' ')
222 .take (longest_typename - extended_state_types[i].len())
223 .collect();
224
225 if !hide_defaults && !extended_state_defaults[i].is_empty() {
226 extended_string.push_str (escape (format!(
227 "{}{} : {}{} = {}",
228 f, spacer1, extended_state_types[i], spacer2, extended_state_defaults[i]
229 )).as_str());
230 } else {
231 extended_string.push_str (escape (format!(
232 "{}{} : {}", f, spacer1, extended_state_types[i]
233 )).as_str());
234 }
235 extended_string.push_str (format!("{}", separator).as_str());
236 }
237
238 let len = extended_string.len();
239 extended_string.truncate (len - separator.len());
240 s.push_str (format!("{}", extended_string).as_str());
241 } s.push_str ("<BR ALIGN=\"LEFT\"/>");
244 let self_reference = M::self_reference();
245 if !self_reference.is_empty() && mono_font {
246 s.push_str (format!("@ {}<BR ALIGN=\"CENTER\"/>", self_reference).as_str());
247 }
248 if !extended_state_names.is_empty() {
249 s.push_str ("\n ");
250 }
251
252 if mono_font {
255 s.push_str ("</FONT><BR/>");
256 }
257 s.push_str (">\
258 \n shape=record\
259 \n style=rounded\
260 \n fontname=\"Sans Bold Italic\"\n");
261 } s.push_str (
268 " INITIAL [label=\"\", shape=circle, width=0.2, \
269 style=filled, fillcolor=black]\n");
270 let state_data_names = M::state_data_names();
272 let state_data_types = M::state_data_types();
273 let state_data_defaults : Vec <Vec <String>> = if !pretty_defaults {
274 M::state_data_defaults().into_iter().map (
275 |v| v.into_iter().map (str::to_string).collect()
276 ).collect()
277 } else {
278 let pretty_defaults = M::state_data_pretty_defaults();
279 pretty_defaults.into_iter().map (
280 |v| v.into_iter().map (|pretty_newline| {
281 let mut pretty_br = String::new();
282 let separator = "<BR ALIGN=\"LEFT\"/>\n";
283 for line in pretty_newline.lines() {
284 pretty_br.push_str (escape (line.to_string()).as_str());
285 pretty_br.push_str (separator);
286 }
287 let len = pretty_br.len();
288 pretty_br.truncate (len - separator.len());
289 pretty_br
290 }).collect()
291 ).collect()
292 };
293 debug_assert_eq!(state_data_names.len(), state_data_types.len());
294 debug_assert_eq!(state_data_types.len(), state_data_defaults.len());
295
296 for (i, state) in M::states().iter().enumerate() {
298 let mut mono_font = false;
299 let state_data_names = &state_data_names[i];
300 let state_data_types = &state_data_types[i];
301 let state_data_defaults = &state_data_defaults[i];
302 debug_assert_eq!(state_data_names.len(), state_data_types.len());
303 debug_assert_eq!(state_data_types.len(), state_data_defaults.len());
304 s.push_str (format!(" {} [label=<<B>{}</B>", state, state).as_str());
305 if !state_data_names.is_empty() {
308 if !mono_font {
309 s.push_str ("|<FONT FACE=\"Mono\"><BR/>\n");
310 mono_font = true;
311 }
312 let mut data_string = String::new();
313 let separator = ",<BR ALIGN=\"LEFT\"/>\n";
314 let longest_fieldname = state_data_names.iter().fold (
315 0, |longest, ref fieldname| std::cmp::max (longest, fieldname.len())
316 );
317 let longest_typename = state_data_types.iter().fold (
318 0, |longest, ref typename| std::cmp::max (longest, typename.len())
319 );
320 for (i,f) in state_data_names.iter().enumerate() {
321 let spacer1 : String = std::iter::repeat (' ')
322 .take(longest_fieldname - f.len())
323 .collect();
324 let spacer2 : String = std::iter::repeat (' ')
325 .take(longest_typename - state_data_types[i].len())
326 .collect();
327 if !hide_defaults && !state_data_defaults[i].is_empty() {
328 data_string.push_str (escape (format!(
329 "{}{} : {}{} = {}",
330 f, spacer1, state_data_types[i], spacer2, state_data_defaults[i]
331 )).as_str());
332 } else {
333 data_string.push_str (escape (format!(
334 "{}{} : {}", f, spacer1, state_data_types[i]
335 )).as_str());
336 }
337 data_string.push_str (format!("{}", separator).as_str());
338 }
339 let len = data_string.len();
340 data_string.truncate (len - separator.len());
341 s.push_str (format!("{}", data_string).as_str());
342 }
343
344 if mono_font {
357 s.push_str ("<BR ALIGN=\"LEFT\"/></FONT>");
358 }
359 s.push_str (">]\n");
360 } s.push_str (format!(
369 " INITIAL -> {}\n", M::state_initial()).as_str());
370 let event_sources = M::event_sources();
371 let event_targets = M::event_targets();
372 let event_actions = M::event_actions();
373 let mut universal = false;
374 for (i, event) in M::events().into_iter().enumerate() {
376 let source = event_sources[i];
377 let mut target = event_targets[i];
378 let action = event_actions[i];
379 if target.is_empty() { target = source;
381 }
382
383 if source == "*" {
384 universal = true;
385 }
386 s.push_str (format!(
387 " \"{}\" -> \"{}\" [label=<<FONT FACE=\"Sans Italic\">{}</FONT>",
388 source, target, event
389 ).as_str());
390
391 let mut mono_font = false;
392 if !hide_actions && !action.is_empty() {
398 match action {
399 "{}" | "{ }" => {}
401 _ => {
402 if !mono_font {
403 s.push_str ("<FONT FACE=\"Mono\"><BR/>");
404 mono_font = true;
405 }
406 let action_string = {
408 let mut s : String = action.split_whitespace().map (
409 |s| {
410 let mut s = s.to_string();
411 s.push (' ');
412 s
413 }
414 ).collect();
415 assert_eq!(s.pop(), Some (' '));
416 s
417 };
418 s.push_str (format!("{}", escape (action_string)).as_str());
421 }
422 }
423 }
424
425 if mono_font {
426 s.push_str ("</FONT>");
427 }
428 s.push_str (">]\n");
429 } if universal {
432 for state in M::states() {
433 s.push_str (format!(
434 " {} -> \"*\" [style=dashed, color=gray]", state).as_str());
435 }
436 }
437
438 let state_terminal = M::state_terminal();
441 if !state_terminal.is_empty() {
442 s.push_str (
443 " TERMINAL [label=\"\", shape=doublecircle, width=0.2,\
444 \n style=filled, fillcolor=black]\n");
445 s.push_str (format!(
446 " {} -> TERMINAL\n", state_terminal).as_str());
447 }
448 s.push_str (
454 " }\n\
455 }");
456 s
457} #[inline]
461fn escape (s : String) -> String {
462 use marksman_escape::Escape;
463 String::from_utf8 (Escape::new (s.bytes()).collect()).unwrap()
464}
465
466#[cfg(doc)]
467pub mod example {
468 use crate::def_machine_debug;
470 def_machine_debug! {
471 Door (open_count : u64) @ door {
472 STATES [
473 state Closed (knock_count : u64) {
474 exit { println!("final knock count: {}", knock_count); }
475 }
476 state Opened () {
477 entry { println!("open count: {}", open_count); }
478 }
479 ]
480 EVENTS [
481 event Knock <Closed> () { knock_count } => { *knock_count += 1; }
482 event Open <Closed> => <Opened> () {} => { *open_count += 1; }
483 event Close <Opened> => <Closed> ()
484 ]
485 initial_state: Closed {
486 initial_action: { println!("hello"); }
487 }
488 terminal_state: Closed {
489 terminate_success: { println!("goodbye") }
490 terminate_failure: {
491 panic!("door was left: {:?}", door.state())
492 }
493 }
494 }
495 }
496}
497
498#[cfg(test)]
499mod tests {
500 use super::*;
501 #[test]
502 fn test_initial() {
503 {
504 def_machine!{
505 Test () {
506 STATES [ state A () ]
507 EVENTS [ ]
508 initial_state: A
509 }
510 }
511 let test = Test::initial();
512 assert_eq!(test.state_id(), StateId::A);
513 } {
514 def_machine_debug!{
515 Test () {
516 STATES [ state A () ]
517 EVENTS [ ]
518 initial_state: A
519 }
520 }
521 let test = Test::initial();
522 assert_eq!(test.state_id(), StateId::A);
523 }
524 }
525 #[test]
526 fn test_new() {
527 {
528 def_machine!{
529 Test () {
530 STATES [ state A () ]
531 EVENTS [ ]
532 initial_state: A
533 }
534 }
535 let test = Test::new (ExtendedState::new());
536 assert_eq!(test.state_id(), StateId::A);
537 } {
538 def_machine_debug!{
539 Test () {
540 STATES [ state A () ]
541 EVENTS [ ]
542 initial_state: A
543 }
544 }
545 let test = Test::new (ExtendedState::new());
546 assert_eq!(test.state_id(), StateId::A);
547 } {
548 def_machine_nodefault!{
549 Test () {
550 STATES [ state A () ]
551 EVENTS [ ]
552 initial_state: A
553 }
554 }
555 let test = Test::new (ExtendedState::new().unwrap());
556 assert_eq!(test.state_id(), StateId::A);
557 } {
558 def_machine_nodefault_debug!{
559 Test () {
560 STATES [ state A () ]
561 EVENTS [ ]
562 initial_state: A
563 }
564 }
565 let test = Test::new (ExtendedState::new().unwrap());
566 assert_eq!(test.state_id(), StateId::A);
567 }
568 }
569 #[test]
570 fn test_event_internal() {
571 {
572 def_machine!{
573 Test () {
574 STATES [ state A () ]
575 EVENTS [ event E <A> () ]
576 initial_state: A
577 }
578 }
579 let mut test = Test::initial();
580 test.handle_event (EventId::E.into()).unwrap();
581 } {
582 def_machine_debug!{
583 Test () {
584 STATES [ state A () ]
585 EVENTS [ event E <A> () ]
586 initial_state: A
587 }
588 }
589 let mut test = Test::initial();
590 test.handle_event (EventId::E.into()).unwrap();
591 } {
592 def_machine_nodefault!{
593 Test () {
594 STATES [ state A () ]
595 EVENTS [ event E <A> () ]
596 initial_state: A
597 }
598 }
599 let mut test = Test::new (ExtendedState::new().unwrap());
600 test.handle_event (EventParams::E{}.into()).unwrap();
601 } {
602 def_machine_nodefault_debug!{
603 Test () {
604 STATES [ state A () ]
605 EVENTS [ event E <A> () ]
606 initial_state: A
607 }
608 }
609 let mut test = Test::new (ExtendedState::new().unwrap());
610 test.handle_event (EventParams::E{}.into()).unwrap();
611 }
612 }
613 #[test]
614 fn test_event_external() {
615 {
616 def_machine!{
617 Test () {
618 STATES [
619 state A ()
620 state B ()
621 ]
622 EVENTS [ event E <A> => <B> () ]
623 initial_state: A
624 }
625 }
626 let mut test = Test::initial();
627 test.handle_event (EventId::E.into()).unwrap();
628 assert_eq!(test.state_id(), StateId::B);
629 } {
630 def_machine_debug!{
631 Test () {
632 STATES [
633 state A ()
634 state B ()
635 ]
636 EVENTS [ event E <A> => <B> () ]
637 initial_state: A
638 }
639 }
640 let mut test = Test::initial();
641 test.handle_event (EventId::E.into()).unwrap();
642 assert_eq!(test.state_id(), StateId::B);
643 } {
644 def_machine_nodefault!{
645 Test () {
646 STATES [
647 state A ()
648 state B ()
649 ]
650 EVENTS [ event E <A> => <B> () ]
651 initial_state: A
652 }
653 }
654 let mut test = Test::new (ExtendedState::new().unwrap());
655 test.handle_event (EventParams::E{}.into()).unwrap();
656 assert_eq!(test.state_id(), StateId::B);
657 } {
658 def_machine_nodefault_debug!{
659 Test () {
660 STATES [
661 state A ()
662 state B ()
663 ]
664 EVENTS [ event E <A> => <B> () ]
665 initial_state: A
666 }
667 }
668 let mut test = Test::new (ExtendedState::new().unwrap());
669 test.handle_event (EventParams::E{}.into()).unwrap();
670 assert_eq!(test.state_id(), StateId::B);
671 }
672 }
673}