Reorganize GSP module layout
This commit is contained in:
103
toolkit/internal/gsp/graph.go
Normal file
103
toolkit/internal/gsp/graph.go
Normal file
@@ -0,0 +1,103 @@
|
||||
package gsp
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func (p *Project) Graph(id string, depth int) Graph {
|
||||
if id != "" {
|
||||
flattened := p.Flatten(id, depth, Filter{})
|
||||
return p.graphForUnits(flattened.Units)
|
||||
}
|
||||
return p.graphForUnits(p.Units)
|
||||
}
|
||||
|
||||
func (p *Project) graphForUnits(units []*Unit) Graph {
|
||||
nodeMap := map[string]GraphNode{}
|
||||
edgeMap := map[string]GraphEdge{}
|
||||
include := map[string]bool{}
|
||||
for _, unit := range units {
|
||||
include[unit.ID] = true
|
||||
nodeMap[unit.ID] = GraphNode{ID: unit.ID, Type: unit.Type, Resolution: unit.Resolution, File: unit.File}
|
||||
}
|
||||
for _, unit := range units {
|
||||
if unit.Refines != "" {
|
||||
addEdge(edgeMap, unit.ID, unit.Refines, "refines")
|
||||
if !include[unit.Refines] {
|
||||
nodeMap[unit.Refines] = GraphNode{ID: unit.Refines, Missing: p.ByID[unit.Refines] == nil}
|
||||
}
|
||||
}
|
||||
for _, rel := range unit.With {
|
||||
addEdge(edgeMap, unit.ID, rel.ID, "with")
|
||||
if !include[rel.ID] {
|
||||
nodeMap[rel.ID] = GraphNode{ID: rel.ID, Missing: p.ByID[rel.ID] == nil}
|
||||
}
|
||||
}
|
||||
}
|
||||
nodes := make([]GraphNode, 0, len(nodeMap))
|
||||
for _, node := range nodeMap {
|
||||
nodes = append(nodes, node)
|
||||
}
|
||||
sort.Slice(nodes, func(i, j int) bool {
|
||||
return nodes[i].ID < nodes[j].ID
|
||||
})
|
||||
edges := make([]GraphEdge, 0, len(edgeMap))
|
||||
for _, edge := range edgeMap {
|
||||
edges = append(edges, edge)
|
||||
}
|
||||
sort.Slice(edges, func(i, j int) bool {
|
||||
if edges[i].From == edges[j].From {
|
||||
if edges[i].To == edges[j].To {
|
||||
return edges[i].Kind < edges[j].Kind
|
||||
}
|
||||
return edges[i].To < edges[j].To
|
||||
}
|
||||
return edges[i].From < edges[j].From
|
||||
})
|
||||
return Graph{Nodes: nodes, Edges: edges}
|
||||
}
|
||||
|
||||
func addEdge(edges map[string]GraphEdge, from, to, kind string) {
|
||||
key := from + "\x00" + to + "\x00" + kind
|
||||
edges[key] = GraphEdge{From: from, To: to, Kind: kind}
|
||||
}
|
||||
|
||||
var mermaidIDPattern = regexp.MustCompile(`[^A-Za-z0-9_]`)
|
||||
|
||||
func (g Graph) Mermaid() string {
|
||||
var builder strings.Builder
|
||||
builder.WriteString("graph TD\n")
|
||||
if len(g.Nodes) == 0 {
|
||||
return builder.String()
|
||||
}
|
||||
for _, node := range g.Nodes {
|
||||
label := node.ID
|
||||
if node.Missing {
|
||||
label += " (missing)"
|
||||
}
|
||||
builder.WriteString(fmt.Sprintf(" %s[\"%s\"]\n", mermaidID(node.ID), escapeMermaid(label)))
|
||||
}
|
||||
for _, edge := range g.Edges {
|
||||
builder.WriteString(fmt.Sprintf(" %s -- %s --> %s\n", mermaidID(edge.From), edge.Kind, mermaidID(edge.To)))
|
||||
}
|
||||
return builder.String()
|
||||
}
|
||||
|
||||
func mermaidID(id string) string {
|
||||
value := mermaidIDPattern.ReplaceAllString(id, "_")
|
||||
if value == "" {
|
||||
return "node"
|
||||
}
|
||||
if value[0] >= '0' && value[0] <= '9' {
|
||||
value = "n_" + value
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
func escapeMermaid(value string) string {
|
||||
value = strings.ReplaceAll(value, `"`, `\"`)
|
||||
return value
|
||||
}
|
||||
Reference in New Issue
Block a user