mpagenko | bb47bc2 | 2021-04-20 13:29:09 +0000 | [diff] [blame] | 1 | package fsm |
| 2 | |
| 3 | import ( |
| 4 | "bytes" |
| 5 | "fmt" |
| 6 | ) |
| 7 | |
| 8 | // Visualize outputs a visualization of a FSM in Graphviz format. |
| 9 | func Visualize(fsm *FSM) string { |
| 10 | var buf bytes.Buffer |
| 11 | |
| 12 | // we sort the key alphabetically to have a reproducible graph output |
| 13 | sortedEKeys := getSortedTransitionKeys(fsm.transitions) |
| 14 | sortedStateKeys, _ := getSortedStates(fsm.transitions) |
| 15 | |
| 16 | writeHeaderLine(&buf) |
| 17 | writeTransitions(&buf, fsm.current, sortedEKeys, fsm.transitions) |
| 18 | writeStates(&buf, sortedStateKeys) |
| 19 | writeFooter(&buf) |
| 20 | |
| 21 | return buf.String() |
| 22 | } |
| 23 | |
| 24 | func writeHeaderLine(buf *bytes.Buffer) { |
| 25 | buf.WriteString(fmt.Sprintf(`digraph fsm {`)) |
| 26 | buf.WriteString("\n") |
| 27 | } |
| 28 | |
| 29 | func writeTransitions(buf *bytes.Buffer, current string, sortedEKeys []eKey, transitions map[eKey]string) { |
| 30 | // make sure the current state is at top |
| 31 | for _, k := range sortedEKeys { |
| 32 | if k.src == current { |
| 33 | v := transitions[k] |
| 34 | buf.WriteString(fmt.Sprintf(` "%s" -> "%s" [ label = "%s" ];`, k.src, v, k.event)) |
| 35 | buf.WriteString("\n") |
| 36 | } |
| 37 | } |
| 38 | for _, k := range sortedEKeys { |
| 39 | if k.src != current { |
| 40 | v := transitions[k] |
| 41 | buf.WriteString(fmt.Sprintf(` "%s" -> "%s" [ label = "%s" ];`, k.src, v, k.event)) |
| 42 | buf.WriteString("\n") |
| 43 | } |
| 44 | } |
| 45 | |
| 46 | buf.WriteString("\n") |
| 47 | } |
| 48 | |
| 49 | func writeStates(buf *bytes.Buffer, sortedStateKeys []string) { |
| 50 | for _, k := range sortedStateKeys { |
| 51 | buf.WriteString(fmt.Sprintf(` "%s";`, k)) |
| 52 | buf.WriteString("\n") |
| 53 | } |
| 54 | } |
| 55 | |
| 56 | func writeFooter(buf *bytes.Buffer) { |
| 57 | buf.WriteString(fmt.Sprintln("}")) |
| 58 | } |