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  }