-
Notifications
You must be signed in to change notification settings - Fork 9
/
Copy pathvisualiser.go
132 lines (110 loc) · 3.08 KB
/
visualiser.go
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
package workflow
import (
"errors"
"os"
"strings"
"text/template"
"github.com/luno/workflow/internal/util"
)
// CreateDiagram creates a diagram in a md file for communicating a workflow's set of steps in an easy-to-understand
// manner.
func CreateDiagram[Type any, Status StatusType](a API[Type, Status], path string, d MermaidDirection) error {
return mermaidDiagram[Type, Status](a, path, d)
}
func mermaidDiagram[Type any, Status StatusType](a API[Type, Status], path string, d MermaidDirection) error {
w, ok := a.(*Workflow[Type, Status])
if !ok {
return errors.New("cannot create diagram for non-original workflow.Workflow type")
}
breakDown := strings.Split(path, "/")
dirPath := strings.Join(breakDown[:len(breakDown)-1], "/")
err := os.MkdirAll(dirPath, 0755)
if err != nil {
return err
}
fileName := breakDown[len(breakDown)-1:][0]
file, err := os.Create(dirPath + "/" + fileName)
if err != nil {
return err
}
if d == UnknownDirection {
d = LeftToRightDirection
}
graphInfo := w.statusGraph.Info()
var starting []int
for _, node := range graphInfo.StartingNodes {
starting = append(starting, node)
}
var terminal []int
for _, node := range graphInfo.TerminalNodes {
terminal = append(terminal, node)
}
var transitions []MermaidTransition
dedupe := make(map[int]bool)
for _, transition := range graphInfo.Transitions {
if dedupe[transition.From] {
continue
}
transitions = append(transitions, MermaidTransition{
From: transition.From,
To: w.statusGraph.Transitions(transition.From),
})
dedupe[transition.From] = true
}
mf := MermaidFormat{
WorkflowName: w.Name(),
Direction: d,
StartingPoints: starting,
TerminalPoints: terminal,
Transitions: transitions,
Nodes: w.statusGraph.Nodes(),
}
return template.Must(template.New("").Funcs(map[string]any{
"Description": description[Status],
}).Parse("```"+mermaidTemplate+"```")).Execute(file, mf)
}
func description[Status StatusType](val int) string {
s := Status(val).String()
return util.CamelCaseToSpacing(s)
}
type MermaidFormat struct {
WorkflowName string
Direction MermaidDirection
Nodes []int
StartingPoints []int
TerminalPoints []int
Transitions []MermaidTransition
}
type MermaidDirection string
const (
UnknownDirection MermaidDirection = ""
TopToBottomDirection MermaidDirection = "TB"
LeftToRightDirection MermaidDirection = "LR"
RightToLeftDirection MermaidDirection = "RL"
BottomToTopDirection MermaidDirection = "BT"
)
type MermaidTransition struct {
From int
To []int
}
var mermaidTemplate = `mermaid
---
title: Workflow diagram of {{.WorkflowName}}
---
stateDiagram-v2
direction {{.Direction}}
{{range $key, $value := .Nodes }}
{{$value}}: {{Description $value}}
{{- end }}
{{ range $key, $value := .Transitions }}
{{- if gt (len $value.To) 1 }}
state {{$value.From}}_branching <<choice>>
{{$value.From}} --> {{$value.From}}_branching
{{- range $index, $to := $value.To }}
{{$value.From}}_branching --> {{$to}}
{{- end}}
{{ else }}
{{$value.From}}-->{{index $value.To 0}}
{{- end}}
{{- end }}
`