github.com/olivere/camlistore@v0.0.0-20140121221811-1b7ac2da0199/cmd/camget/graph.go (about) 1 /* 2 Copyright 2012 Google Inc. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package main 18 19 import ( 20 "fmt" 21 "io" 22 "log" 23 "strings" 24 "sync" 25 26 "camlistore.org/pkg/blob" 27 "camlistore.org/pkg/index" 28 "camlistore.org/pkg/schema" 29 ) 30 31 func check(err error) { 32 if err != nil { 33 log.Fatal(err) 34 } 35 } 36 37 type node struct { 38 br blob.Ref 39 g *graph 40 41 size int64 42 blob *schema.Blob 43 edges []blob.Ref 44 } 45 46 func (n *node) dotName() string { 47 return strings.Replace(n.br.String(), "-", "_", -1) 48 } 49 50 func (n *node) dotLabel() string { 51 name := n.displayName() 52 if n.blob == nil { 53 return fmt.Sprintf("%s\n%d bytes", name, n.size) 54 } 55 return name + "\n" + n.blob.Type() 56 } 57 58 func (n *node) color() string { 59 if n.br == n.g.root { 60 return "#a0ffa0" 61 } 62 if n.blob == nil { 63 return "#aaaaaa" 64 } 65 return "#a0a0ff" 66 } 67 68 func (n *node) displayName() string { 69 s := n.br.String() 70 s = s[strings.Index(s, "-")+1:] 71 return s[:7] 72 } 73 74 func (n *node) load() { 75 defer n.g.wg.Done() 76 rc, err := fetch(n.g.src, n.br) 77 check(err) 78 defer rc.Close() 79 sniff := index.NewBlobSniffer(n.br) 80 n.size, err = io.Copy(sniff, rc) 81 check(err) 82 sniff.Parse() 83 blob, ok := sniff.SchemaBlob() 84 if !ok { 85 return 86 } 87 n.blob = blob 88 for _, part := range blob.ByteParts() { 89 n.addEdge(part.BlobRef) 90 n.addEdge(part.BytesRef) 91 } 92 } 93 94 func (n *node) addEdge(dst blob.Ref) { 95 if !dst.Valid() { 96 return 97 } 98 n.g.startLoadNode(dst) 99 n.edges = append(n.edges, dst) 100 } 101 102 type graph struct { 103 src blob.StreamingFetcher 104 root blob.Ref 105 106 mu sync.Mutex // guards n 107 n map[string]*node 108 109 wg sync.WaitGroup 110 } 111 112 func (g *graph) startLoadNode(br blob.Ref) { 113 g.mu.Lock() 114 defer g.mu.Unlock() 115 key := br.String() 116 if _, ok := g.n[key]; ok { 117 return 118 } 119 n := &node{ 120 g: g, 121 br: br, 122 } 123 g.n[key] = n 124 g.wg.Add(1) 125 go n.load() 126 } 127 128 func printGraph(src blob.StreamingFetcher, root blob.Ref) { 129 g := &graph{ 130 src: src, 131 root: root, 132 n: make(map[string]*node), 133 } 134 g.startLoadNode(root) 135 g.wg.Wait() 136 fmt.Println("digraph G {") 137 fmt.Println(" node [fontsize=10,fontname=Arial]") 138 fmt.Println(" edge [fontsize=10,fontname=Arial]") 139 140 for _, n := range g.n { 141 fmt.Printf("\n %s [label=%q,style=filled,fillcolor=%q]\n", n.dotName(), n.dotLabel(), n.color()) 142 for i, e := range n.edges { 143 // TODO: create an edge type. 144 // Also, this edgeLabel is specific to file parts. Other schema 145 // types might not even have a concept of ordering. This is hack. 146 edgeLabel := fmt.Sprintf("%d", i) 147 if i == 0 { 148 edgeLabel = "first" 149 } else if i == len(n.edges)-1 { 150 edgeLabel = "last" 151 } 152 fmt.Printf(" %s -> %s [label=%q]\n", n.dotName(), g.n[e.String()].dotName(), edgeLabel) 153 } 154 } 155 fmt.Printf("}\n") 156 }