github.com/gnolang/gno@v0.0.0-20240520182011-228e9d0192ce/tm2/pkg/iavl/tree_dotgraph.go (about) 1 package iavl 2 3 import ( 4 "bytes" 5 "fmt" 6 "io" 7 "text/template" 8 ) 9 10 type graphEdge struct { 11 From, To string 12 } 13 14 type graphNode struct { 15 Hash string 16 Label string 17 Value string 18 Attrs map[string]string 19 } 20 21 type graphContext struct { 22 Edges []*graphEdge 23 Nodes []*graphNode 24 } 25 26 var graphTemplate = ` 27 strict graph { 28 {{- range $i, $edge := $.Edges}} 29 "{{ $edge.From }}" -- "{{ $edge.To }}"; 30 {{- end}} 31 32 {{range $i, $node := $.Nodes}} 33 "{{ $node.Hash }}" [label=<{{ $node.Label }}>,{{ range $k, $v := $node.Attrs }}{{ $k }}={{ $v }},{{end}}]; 34 {{- end}} 35 } 36 ` 37 38 var tpl = template.Must(template.New("iavl").Parse(graphTemplate)) 39 40 var defaultGraphNodeAttrs = map[string]string{ 41 "shape": "circle", 42 } 43 44 func WriteDOTGraph(w io.Writer, tree *ImmutableTree, paths []PathToLeaf) { 45 ctx := &graphContext{} 46 47 tree.root.hashWithCount() 48 tree.root.traverse(tree, true, func(node *Node) bool { 49 graphNode := &graphNode{ 50 Attrs: map[string]string{}, 51 Hash: fmt.Sprintf("%x", node.hash), 52 } 53 for k, v := range defaultGraphNodeAttrs { 54 graphNode.Attrs[k] = v 55 } 56 shortHash := graphNode.Hash[:7] 57 58 graphNode.Label = mkLabel(fmt.Sprintf("%s", node.key), 16, "sans-serif") 59 graphNode.Label += mkLabel(shortHash, 10, "monospace") 60 graphNode.Label += mkLabel(fmt.Sprintf("version=%d", node.version), 10, "monospace") 61 62 if node.value != nil { 63 graphNode.Label += mkLabel(string(node.value), 10, "sans-serif") 64 } 65 66 if node.height == 0 { 67 graphNode.Attrs["fillcolor"] = "lightgrey" 68 graphNode.Attrs["style"] = "filled" 69 } 70 71 for _, path := range paths { 72 for _, n := range path { 73 if bytes.Equal(n.Left, node.hash) || bytes.Equal(n.Right, node.hash) { 74 graphNode.Attrs["peripheries"] = "2" 75 graphNode.Attrs["style"] = "filled" 76 graphNode.Attrs["fillcolor"] = "lightblue" 77 break 78 } 79 } 80 } 81 ctx.Nodes = append(ctx.Nodes, graphNode) 82 83 if node.leftNode != nil { 84 ctx.Edges = append(ctx.Edges, &graphEdge{ 85 From: graphNode.Hash, 86 To: fmt.Sprintf("%x", node.leftNode.hash), 87 }) 88 } 89 if node.rightNode != nil { 90 ctx.Edges = append(ctx.Edges, &graphEdge{ 91 From: graphNode.Hash, 92 To: fmt.Sprintf("%x", node.rightNode.hash), 93 }) 94 } 95 return false 96 }) 97 98 if err := tpl.Execute(w, ctx); err != nil { 99 panic(err) 100 } 101 } 102 103 func mkLabel(label string, pt int, face string) string { 104 return fmt.Sprintf("<font face='%s' point-size='%d'>%s</font><br />", face, pt, label) 105 }