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 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156
|
package graph
import (
"context"
"flag"
"fmt"
"os"
"strconv"
"strings"
"github.com/google/subcommands"
"github.com/loov/goda/internal/pkggraph"
"github.com/loov/goda/internal/pkgset"
"github.com/loov/goda/internal/templates"
)
type Command struct {
printStandard bool
docs string
outputType string
labelFormat string
nocolor bool
clusters bool
shortID bool
}
func (*Command) Name() string { return "graph" }
func (*Command) Synopsis() string { return "Print dependency graph." }
func (*Command) Usage() string {
return `graph <expr>:
Print dependency dot graph.
Supported output types:
dot - GraphViz dot format
graphml - GraphML format
tgf - Trivial Graph Format
edges - format with each edge separately
digraph - format with each node and its edges on a single line
See "help expr" for further information about expressions.
See "help format" for further information about formatting.
`
}
func (cmd *Command) SetFlags(f *flag.FlagSet) {
f.BoolVar(&cmd.printStandard, "std", false, "print std packages")
f.BoolVar(&cmd.nocolor, "nocolor", false, "disable coloring")
f.StringVar(&cmd.docs, "docs", "https://pkg.go.dev/", "override the docs url to use")
f.StringVar(&cmd.outputType, "type", "dot", "output type (dot, graphml, digraph, edges, tgf)")
f.StringVar(&cmd.labelFormat, "f", "", "label formatting")
f.BoolVar(&cmd.clusters, "cluster", false, "create clusters")
f.BoolVar(&cmd.shortID, "short", false, "use short package id-s inside clusters")
}
func (cmd *Command) Execute(ctx context.Context, f *flag.FlagSet, _ ...interface{}) subcommands.ExitStatus {
if cmd.labelFormat == "" {
switch cmd.outputType {
case "dot":
cmd.labelFormat = `{{.ID}}\l{{ .Stat.Go.Lines }} / {{ .Stat.Go.Size }}\l`
default:
cmd.labelFormat = `{{.ID}}`
}
}
label, err := templates.Parse(cmd.labelFormat)
if err != nil {
fmt.Fprintf(os.Stderr, "invalid label format: %v\n", err)
return subcommands.ExitFailure
}
var format Format
switch strings.ToLower(cmd.outputType) {
case "dot":
format = &Dot{
out: os.Stdout,
err: os.Stderr,
docs: cmd.docs,
clusters: cmd.clusters,
nocolor: cmd.nocolor,
shortID: cmd.shortID,
label: label,
}
case "digraph":
format = &Digraph{
out: os.Stdout,
err: os.Stderr,
label: label,
}
case "tgf":
format = &TGF{
out: os.Stdout,
err: os.Stderr,
label: label,
}
case "edges":
format = &Edges{
out: os.Stdout,
err: os.Stderr,
label: label,
}
case "graphml":
format = &GraphML{
out: os.Stdout,
err: os.Stderr,
label: label,
}
default:
fmt.Fprintf(os.Stderr, "unknown output type %q\n", cmd.outputType)
return subcommands.ExitFailure
}
if !cmd.printStandard {
go pkgset.LoadStd()
}
result, err := pkgset.Calc(ctx, f.Args())
if err != nil {
fmt.Fprintln(os.Stderr, err.Error())
return subcommands.ExitFailure
}
if !cmd.printStandard {
result = pkgset.Subtract(result, pkgset.Std())
}
graph := pkggraph.From(result)
if err := format.Write(graph); err != nil {
fmt.Fprintf(os.Stderr, "error building graph: %v\n", err)
return subcommands.ExitFailure
}
return subcommands.ExitSuccess
}
type Format interface {
Write(*pkggraph.Graph) error
}
func pkgID(p *pkggraph.Node) string {
// Go quoting rules are similar enough to dot quoting.
// At least enough similar to quote a Go import path.
return strconv.Quote(p.ID)
}
|