forked from poloxue/modv
-
Notifications
You must be signed in to change notification settings - Fork 0
/
graph.go
101 lines (84 loc) · 1.9 KB
/
graph.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
package main
import (
"bufio"
"bytes"
"fmt"
"io"
"strings"
"text/template"
)
var graphTemplate = `digraph {
{{- if eq .direction "horizontal" -}}
rankdir=LR;
{{ end -}}
node [shape=box];
{{ range $mod, $modId := .mods -}}
{{ $modId }} [label="{{ $mod }}"];
{{ end -}}
{{- range $modId, $depModIds := .dependencies -}}
{{- range $_, $depModId := $depModIds -}}
{{ $modId }} -> {{ $depModId }};
{{ end -}}
{{- end -}}
}
`
type ModuleGraph struct {
Reader io.Reader
Mods map[string]int
Dependencies map[int][]int
}
func NewModuleGraph(r io.Reader) *ModuleGraph {
return &ModuleGraph{
Reader: r,
Mods: make(map[string]int),
Dependencies: make(map[int][]int),
}
}
func (m *ModuleGraph) Parse() error {
bufReader := bufio.NewReader(m.Reader)
serialID := 1
for {
relationBytes, err := bufReader.ReadBytes('\n')
if err != nil {
if err == io.EOF {
return nil
}
return err
}
relation := bytes.Split(relationBytes, []byte(" "))
mod, depMod := strings.TrimSpace(string(relation[0])), strings.TrimSpace(string(relation[1]))
mod = strings.Replace(mod, "@", "\n", 1)
depMod = strings.Replace(depMod, "@", "\n", 1)
modId, ok := m.Mods[mod]
if !ok {
modId = serialID
m.Mods[mod] = modId
serialID += 1
}
depModId, ok := m.Mods[depMod]
if !ok {
depModId = serialID
m.Mods[depMod] = depModId
serialID += 1
}
m.Dependencies[modId] = append(m.Dependencies[modId], depModId)
}
}
func (m *ModuleGraph) Render(w io.Writer) error {
templ, err := template.New("graph").Parse(graphTemplate)
if err != nil {
return fmt.Errorf("templ.Parse: %v", err)
}
var direction string
if len(m.Dependencies) > 15 {
direction = "horizontal"
}
if err := templ.Execute(w, map[string]interface{}{
"mods": m.Mods,
"dependencies": m.Dependencies,
"direction": direction,
}); err != nil {
return fmt.Errorf("templ.Execute: %v", err)
}
return nil
}