mpagenko | bb47bc2 | 2021-04-20 13:29:09 +0000 | [diff] [blame] | 1 | package fsm |
| 2 | |
| 3 | import ( |
| 4 | "fmt" |
| 5 | "sort" |
| 6 | ) |
| 7 | |
| 8 | // VisualizeType the type of the visualization |
| 9 | type VisualizeType string |
| 10 | |
| 11 | const ( |
| 12 | // GRAPHVIZ the type for graphviz output (http://www.webgraphviz.com/) |
| 13 | GRAPHVIZ VisualizeType = "graphviz" |
| 14 | // MERMAID the type for mermaid output (https://mermaid-js.github.io/mermaid/#/stateDiagram) in the stateDiagram form |
| 15 | MERMAID VisualizeType = "mermaid" |
| 16 | // MERMAID the type for mermaid output (https://mermaid-js.github.io/mermaid/#/stateDiagram) in the stateDiagram form |
| 17 | MermaidStateDiagram VisualizeType = "mermaid-state-diagram" |
| 18 | // MERMAID the type for mermaid output (https://mermaid-js.github.io/mermaid/#/flowchart) in the flow chart form |
| 19 | MermaidFlowChart VisualizeType = "mermaid-flow-chart" |
| 20 | ) |
| 21 | |
| 22 | // VisualizeWithType outputs a visualization of a FSM in the desired format. |
| 23 | // If the type is not given it defaults to GRAPHVIZ |
| 24 | func VisualizeWithType(fsm *FSM, visualizeType VisualizeType) (string, error) { |
| 25 | switch visualizeType { |
| 26 | case GRAPHVIZ: |
| 27 | return Visualize(fsm), nil |
| 28 | case MERMAID: |
| 29 | return VisualizeForMermaidWithGraphType(fsm, StateDiagram) |
| 30 | case MermaidStateDiagram: |
| 31 | return VisualizeForMermaidWithGraphType(fsm, StateDiagram) |
| 32 | case MermaidFlowChart: |
| 33 | return VisualizeForMermaidWithGraphType(fsm, FlowChart) |
| 34 | default: |
| 35 | return "", fmt.Errorf("unknown VisualizeType: %s", visualizeType) |
| 36 | } |
| 37 | } |
| 38 | |
| 39 | func getSortedTransitionKeys(transitions map[eKey]string) []eKey { |
| 40 | // we sort the key alphabetically to have a reproducible graph output |
| 41 | sortedTransitionKeys := make([]eKey, 0) |
| 42 | |
| 43 | for transition := range transitions { |
| 44 | sortedTransitionKeys = append(sortedTransitionKeys, transition) |
| 45 | } |
| 46 | sort.Slice(sortedTransitionKeys, func(i, j int) bool { |
| 47 | if sortedTransitionKeys[i].src == sortedTransitionKeys[j].src { |
| 48 | return sortedTransitionKeys[i].event < sortedTransitionKeys[j].event |
| 49 | } |
| 50 | return sortedTransitionKeys[i].src < sortedTransitionKeys[j].src |
| 51 | }) |
| 52 | |
| 53 | return sortedTransitionKeys |
| 54 | } |
| 55 | |
| 56 | func getSortedStates(transitions map[eKey]string) ([]string, map[string]string) { |
| 57 | statesToIDMap := make(map[string]string) |
| 58 | for transition, target := range transitions { |
| 59 | if _, ok := statesToIDMap[transition.src]; !ok { |
| 60 | statesToIDMap[transition.src] = "" |
| 61 | } |
| 62 | if _, ok := statesToIDMap[target]; !ok { |
| 63 | statesToIDMap[target] = "" |
| 64 | } |
| 65 | } |
| 66 | |
| 67 | sortedStates := make([]string, 0, len(statesToIDMap)) |
| 68 | for state := range statesToIDMap { |
| 69 | sortedStates = append(sortedStates, state) |
| 70 | } |
| 71 | sort.Strings(sortedStates) |
| 72 | |
| 73 | for i, state := range sortedStates { |
| 74 | statesToIDMap[state] = fmt.Sprintf("id%d", i) |
| 75 | } |
| 76 | return sortedStates, statesToIDMap |
| 77 | } |