├── .gitignore ├── fixture ├── sub1 │ └── sub1.go ├── vendor │ └── v1 │ │ └── v1.go ├── sample_test.go └── sample.go ├── .travis.yml ├── README.md ├── report_test.go ├── report.go ├── visitors.go ├── main_test.go └── main.go /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | *.iml 3 | -------------------------------------------------------------------------------- /fixture/sub1/sub1.go: -------------------------------------------------------------------------------- 1 | package sub1 2 | 3 | type Sub struct { 4 | } 5 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | go: 3 | - 1.4 4 | - 1.4.1 5 | - 1.4.2 6 | script: 7 | - go test -v -race 8 | -------------------------------------------------------------------------------- /fixture/vendor/v1/v1.go: -------------------------------------------------------------------------------- 1 | package v1 2 | 3 | //some vendor package 4 | 5 | type Sub struct {} 6 | 7 | func fooBar() { 8 | 9 | } 10 | -------------------------------------------------------------------------------- /fixture/sample_test.go: -------------------------------------------------------------------------------- 1 | package fixture_test 2 | 3 | import ( 4 | . "github.com/smartystreets/goconvey/convey" 5 | "github.com/stretchr/testify/assert" 6 | "testing" 7 | ) 8 | 9 | func TestSomething(t *testing.T) { 10 | Convey("test something", t, func() { 11 | So(1, ShouldEqual, 1) 12 | assert.Equal(t, 1, 1) 13 | }) 14 | } 15 | 16 | func BenchmarkSomething(b *testing.B) { 17 | return; 18 | } 19 | 20 | 21 | func TestMainSomething(m *testing.M) { 22 | return; 23 | } 24 | -------------------------------------------------------------------------------- /fixture/sample.go: -------------------------------------------------------------------------------- 1 | package fixture 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | ) 7 | 8 | /* 9 | four 10 | line block comment 11 | */ 12 | 13 | /* stupid block */ /* comment 14 | spanning 15 | */ /* many lines in a weird way*/ 16 | 17 | //first single line comment 18 | //send single line comment 19 | 20 | type Foo struct { 21 | } 22 | 23 | type Bar struct { 24 | MyProperty int 25 | } 26 | 27 | func (b *Bar) ExportedMethod() { 28 | if 1 == 1 { 29 | return 30 | } 31 | 32 | if 1 == 2 { 33 | return 34 | } else { 35 | return 36 | } 37 | } 38 | 39 | func (b *Bar) unexportedMethod() { 40 | switch "foo" { 41 | case "bar": 42 | return 43 | case "baz": 44 | return 45 | } 46 | } 47 | 48 | type Baz interface { 49 | } 50 | 51 | func ExportedFunction(s string, i int64) { 52 | fmt.Print(os.Args) 53 | } 54 | 55 | func unexportedFunction() { 56 | go fmt.Print("") 57 | go fmt.Print("") 58 | 59 | } 60 | 61 | const FOO = "foo" 62 | const BAR = "bar" 63 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | #GOLOCC 2 | 3 | [![Build Status](https://travis-ci.org/warmans/golocc.svg)](https://travis-ci.org/warmans/golocc) [![code-coverage](http://gocover.io/_badge/github.com/warmans/golocc)](http://gocover.io/github.com/warmans/golocc) 4 | 5 | Utility for counting lines of code (LOC, CLOC, NCLOC) and structures/statements in a go package. 6 | 7 | ### Usage 8 | 9 | Within a package: 10 | 11 | `golocc ./...` 12 | 13 | or 14 | 15 | `golocc --no-vendor ./...` 16 | 17 | or from outside the package: 18 | 19 | `goloc $GOPATH/src/[some package]` 20 | 21 | 22 | ### Options 23 | 24 | | flag | function | 25 | |------|----------------------------------------------------| 26 | | d | Target directory. Defaults to current working dir (deprecated) | 27 | | o | Output format. Supports `text`, `json` | 28 | 29 | 30 | ### Sample `text` output: 31 | 32 | ``` 33 | -------------------------------------------------------------------------------- 34 | Lines of Code: 547 (36 CLOC, 511 NCLOC) 35 | Packages: 4 36 | Imports: 28 37 | Structs: 12 38 | Interfaces: 3 39 | Methods: 11 (9 Exported, 221 LOC, 20 LOC Avg.) 40 | Functions: 24 (21 Exported, 215 LOC, 8 LOC Avg.) 41 | -------------------------------------------------------------------------------- 42 | Ifs: 57 43 | Switches: 2 44 | Go Routines: 2 45 | -------------------------------------------------------------------------------- 46 | Tests: 19 47 | Benchmarks: 1 48 | Assertions: 2 49 | -------------------------------------------------------------------------------- 50 | ``` 51 | -------------------------------------------------------------------------------- /report_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bytes" 5 | "strings" 6 | "testing" 7 | ) 8 | 9 | func TestTextOutput(t *testing.T) { 10 | 11 | buff := &bytes.Buffer{} 12 | report := &TextReport{writer: buff} 13 | res := &Result{ 14 | LOC: 10, 15 | CLOC: 11, 16 | NCLOC: 12, 17 | Struct: 13, 18 | Interface: 14, 19 | Method: 15, 20 | ExportedMethod: 16, 21 | MethodLOC: 17, 22 | Function: 18, 23 | ExportedFunction: 19, 24 | FunctionLOC: 20, 25 | Import: 21, 26 | IfStatement: 22, 27 | SwitchStatement: 23, 28 | GoStatement: 24, 29 | Test: 25, 30 | Assertion: 26} 31 | 32 | report.Print(res) 33 | 34 | if !strings.Contains(buff.String(), "Lines of Code: 10 (11 CLOC, 12 NCLOC)") { 35 | t.Error("Unexpected output in text report (LOC)") 36 | } 37 | 38 | if !strings.Contains(buff.String(), "Imports: 21") { 39 | t.Error("Unexpected output in text report (Imports)") 40 | } 41 | 42 | if !strings.Contains(buff.String(), "Structs: 13") { 43 | t.Error("Unexpected output in text report (Structs)") 44 | } 45 | 46 | if !strings.Contains(buff.String(), "Interfaces: 14") { 47 | t.Error("Unexpected output in text report (Interfaces)") 48 | } 49 | 50 | if !strings.Contains(buff.String(), "Methods: 15 (16 Exported, 17 LOC, 1 LOC Avg.)") { 51 | t.Error("Unexpected output in text report (Methods)") 52 | } 53 | 54 | if !strings.Contains(buff.String(), "Functions: 18 (19 Exported, 20 LOC, 1 LOC Avg.)") { 55 | t.Error("Unexpected output in text report (Functions)") 56 | } 57 | 58 | if !strings.Contains(buff.String(), "Ifs: 22") { 59 | t.Error("Unexpected output in text report (Ifs)") 60 | } 61 | 62 | if !strings.Contains(buff.String(), "Switches: 23") { 63 | t.Error("Unexpected output in text report (Switches)") 64 | } 65 | 66 | if !strings.Contains(buff.String(), "Go Routines: 24") { 67 | t.Error("Unexpected output in text report (Go Routines)") 68 | } 69 | 70 | if !strings.Contains(buff.String(), "Tests: 25") { 71 | t.Error("Unexpected output in text report (Tests)") 72 | } 73 | 74 | if !strings.Contains(buff.String(), "Assertions: 26") { 75 | t.Error("Unexpected output in text report (Assertions)") 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /report.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "io" 7 | "log" 8 | "strings" 9 | ) 10 | 11 | //ReportInterface - reports that parse results and print out a report 12 | type ReportInterface interface { 13 | Print(*Result) 14 | } 15 | 16 | //JSONReport json structure for LOC report 17 | type JSONReport struct { 18 | writer io.Writer 19 | } 20 | 21 | //Print - print out parsed report in json format 22 | func (j *JSONReport) Print(res *Result) { 23 | jsonOutput, err := json.MarshalIndent(res, "", " ") 24 | if err != nil { 25 | log.Fatal(err) 26 | } 27 | fmt.Fprint(j.writer, string(jsonOutput)) 28 | } 29 | 30 | //TextReport - plaintext report output 31 | type TextReport struct { 32 | writer io.Writer 33 | } 34 | 35 | //Print - print out plaintext report 36 | func (t *TextReport) Print(res *Result) { 37 | fmt.Fprintf(t.writer, "\n") 38 | fmt.Fprintln(t.writer, strings.Repeat("-", 80)) 39 | fmt.Fprintf(t.writer, "Lines of Code: %v (%v CLOC, %v NCLOC)\n", res.LOC, res.CLOC, res.NCLOC) 40 | fmt.Fprintf(t.writer, "Packages: %v\n", res.Package) 41 | fmt.Fprintf(t.writer, "Imports: %v\n", res.Import) 42 | fmt.Fprintf(t.writer, "Structs: %v\n", res.Struct) 43 | fmt.Fprintf(t.writer, "Interfaces: %v\n", res.Interface) 44 | fmt.Fprintf(t.writer, "Methods: %v (%v Exported, %v LOC, %v LOC Avg.)\n", res.Method, res.ExportedMethod, res.MethodLOC, t.divideBy(res.MethodLOC, res.Method)) 45 | fmt.Fprintf(t.writer, "Functions: %v (%v Exported, %v LOC, %v LOC Avg.)\n", res.Function, res.ExportedFunction, res.FunctionLOC, t.divideBy(res.FunctionLOC, res.Function)) 46 | fmt.Fprintln(t.writer, strings.Repeat("-", 80)) 47 | fmt.Fprintf(t.writer, "Ifs: %v \n", res.IfStatement) 48 | fmt.Fprintf(t.writer, "Switches: %v \n", res.SwitchStatement) 49 | fmt.Fprintf(t.writer, "Go Routines: %v \n", res.GoStatement) 50 | fmt.Fprintln(t.writer, strings.Repeat("-", 80)) 51 | fmt.Fprintf(t.writer, "Tests: %v \n", res.Test) 52 | fmt.Fprintf(t.writer, "Benchmarks: %v \n", res.Benchmark) 53 | fmt.Fprintf(t.writer, "Assertions: %v \n", res.Assertion) 54 | fmt.Println(strings.Repeat("-", 80)) 55 | fmt.Fprintf(t.writer, "\n") 56 | } 57 | 58 | //Safely divide where vivisor might be zero 59 | func (t *TextReport) divideBy(num, dividedBy int) int { 60 | if dividedBy == 0 { 61 | return 0 62 | } 63 | return num / dividedBy 64 | } 65 | -------------------------------------------------------------------------------- /visitors.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "go/ast" 6 | "go/token" 7 | "strings" 8 | ) 9 | 10 | type AstVisitor interface { 11 | Visit(ast.Node) 12 | } 13 | 14 | //TypeVisitor visits types to count structs, interfaces 15 | type TypeVisitor struct { 16 | res *Result 17 | } 18 | 19 | func (v *TypeVisitor) Visit(node ast.Node) { 20 | switch node.(type) { 21 | case *ast.StructType: 22 | v.res.Struct++ 23 | case *ast.InterfaceType: 24 | v.res.Interface++ 25 | } 26 | } 27 | 28 | //FuncVisitor visits functions to count functions, methods tests etc. 29 | type FuncVisitor struct { 30 | res *Result 31 | fset *token.FileSet 32 | } 33 | 34 | func (v *FuncVisitor) Visit(node ast.Node) { 35 | 36 | switch x := node.(type) { 37 | case *ast.FuncDecl: 38 | 39 | nodeLOC := (v.fset.Position(x.End()).Line) - (v.fset.Position(x.Pos()).Line + 1) 40 | 41 | if x.Recv == nil { 42 | //count function 43 | v.res.Function++ 44 | 45 | //count function lines 46 | v.res.FunctionLOC += nodeLOC 47 | 48 | //check if the function is a test 49 | if x.Name.IsExported() { 50 | v.res.ExportedFunction++ 51 | if strings.HasPrefix(x.Name.String(), "Test") || strings.HasPrefix(x.Name.String(), "Benchmark") { 52 | if len(x.Type.Params.List) != 0 { 53 | xt := x.Type.Params.List[0].Type.(*ast.StarExpr) 54 | xtx := xt.X.(*ast.SelectorExpr) 55 | if strings.HasPrefix(x.Name.String(), "Test") { 56 | for _, validArgType := range []string{"testing.T", "testing.M"} { 57 | if fmt.Sprintf("%s.%s", xtx.X, xtx.Sel) == validArgType { 58 | v.res.Test++ 59 | } 60 | } 61 | } 62 | if strings.HasPrefix(x.Name.String(), "Benchmark") { 63 | for _, validArgType := range []string{"testing.B"} { 64 | if fmt.Sprintf("%s.%s", xtx.X, xtx.Sel) == validArgType { 65 | v.res.Benchmark++ 66 | } 67 | } 68 | } 69 | } 70 | } 71 | } 72 | } else { 73 | 74 | v.res.Method++ 75 | 76 | //count method lines 77 | v.res.MethodLOC += nodeLOC 78 | 79 | if x.Name.IsExported() { 80 | v.res.ExportedMethod++ 81 | } 82 | } 83 | } 84 | } 85 | 86 | //ImportVisitor visits import statements 87 | type ImportVisitor struct { 88 | res *Result 89 | } 90 | 91 | func (v *ImportVisitor) Visit(node ast.Node) { 92 | switch node.(type) { 93 | case *ast.ImportSpec: 94 | v.res.Import++ 95 | } 96 | } 97 | 98 | //FlowControlVisitor visits ifs, cases etc. 99 | type FlowControlVisitor struct { 100 | res *Result 101 | } 102 | 103 | func (v *FlowControlVisitor) Visit(node ast.Node) { 104 | switch node.(type) { 105 | case *ast.IfStmt: 106 | v.res.IfStatement++ 107 | case *ast.SwitchStmt: 108 | v.res.SwitchStatement++ 109 | case *ast.GoStmt: 110 | v.res.GoStatement++ 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /main_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestIgnoreVendor(t *testing.T) { 8 | parser1 := Parser{} 9 | result1 := parser1.ParsePackages([]string{"./fixture/..."}, true) 10 | 11 | parser2 := Parser{} 12 | result2 := parser2.ParsePackages([]string{"./fixture/..."}, false) 13 | 14 | if result1.CLOC == result2.CLOC { 15 | t.Error("got same CLOC result with and without vendor dir") 16 | } 17 | } 18 | 19 | func TestCLOC(t *testing.T) { 20 | 21 | parser := Parser{} 22 | result := parser.ParsePackages([]string{"./fixture/..."}, true) 23 | 24 | if result.CLOC != 8 { 25 | t.Error("expected 8 comment lines got", result.CLOC) 26 | } 27 | } 28 | 29 | func TestStructCount(t *testing.T) { 30 | 31 | parser := Parser{} 32 | result := parser.ParsePackages([]string{"./fixture/..."}, true) 33 | 34 | if result.Struct != 3 { 35 | t.Error("expected 3 structs got", result.Struct) 36 | } 37 | } 38 | 39 | func TestInterfaceCount(t *testing.T) { 40 | 41 | parser := Parser{} 42 | result := parser.ParsePackages([]string{"./fixture/..."}, true) 43 | 44 | if result.Interface != 1 { 45 | t.Error("expected 1 interface got", result.Interface) 46 | } 47 | } 48 | 49 | func TestMethodCount(t *testing.T) { 50 | 51 | parser := Parser{} 52 | result := parser.ParsePackages([]string{"./fixture/..."}, true) 53 | 54 | if result.Method != 2 { 55 | t.Error("expected 2 methods") 56 | } 57 | 58 | if result.ExportedMethod != 1 { 59 | t.Error("expected 1 exported method got", result.ExportedMethod) 60 | } 61 | } 62 | 63 | func TestMethodLineCount(t *testing.T) { 64 | 65 | parser := Parser{} 66 | result := parser.ParsePackages([]string{"./fixture/..."}, true) 67 | 68 | if result.MethodLOC != 15 { 69 | t.Error("expected 15 method lines got", result.MethodLOC) 70 | } 71 | } 72 | 73 | func TestFunctionCount(t *testing.T) { 74 | 75 | parser := Parser{} 76 | result := parser.ParsePackages([]string{"./fixture/..."}, true) 77 | 78 | if result.Function != 5 { 79 | t.Error("expected 5 functions got", result.Function) 80 | } 81 | 82 | if result.ExportedFunction != 4 { 83 | t.Error("expected 4 exported functions") 84 | } 85 | } 86 | 87 | func TestFunctionLineCount(t *testing.T) { 88 | 89 | parser := Parser{} 90 | result := parser.ParsePackages([]string{"./fixture/..."}, true) 91 | 92 | if result.FunctionLOC != 10 { 93 | t.Error("expected 10 function lines got", result.FunctionLOC) 94 | } 95 | } 96 | 97 | func TestImportCount(t *testing.T) { 98 | 99 | parser := Parser{} 100 | result := parser.ParsePackages([]string{"./fixture/..."}, true) 101 | 102 | if result.Import != 5 { 103 | t.Error("expected 5 imports got", result.Import) 104 | } 105 | } 106 | 107 | func TestTestCount(t *testing.T) { 108 | parser := Parser{} 109 | result := parser.ParsePackages([]string{"./fixture/..."}, true) 110 | if result.Test != 2 { 111 | t.Error("expected 2 tests got", result.Test) 112 | } 113 | } 114 | 115 | func TestBenchmarkCount(t *testing.T) { 116 | parser := Parser{} 117 | result := parser.ParsePackages([]string{"./fixture/..."}, true) 118 | if result.Benchmark != 1 { 119 | t.Error("expected 1 benchmark got", result.Benchmark) 120 | } 121 | } 122 | 123 | func TestAssertCount(t *testing.T) { 124 | parser := Parser{} 125 | result := parser.ParsePackages([]string{"./fixture/..."}, true) 126 | if result.Assertion != 2 { 127 | t.Error("expected 2 assertions got", result.Assertion) 128 | } 129 | } 130 | 131 | func TestIfStatementCount(t *testing.T) { 132 | parser := Parser{} 133 | result := parser.ParsePackages([]string{"./fixture/..."}, true) 134 | if result.IfStatement != 2 { 135 | t.Error("expected 2 if statements got", result.IfStatement) 136 | } 137 | } 138 | 139 | func TestSwitchStatementCount(t *testing.T) { 140 | parser := Parser{} 141 | result := parser.ParsePackages([]string{"./fixture/..."}, true) 142 | if result.SwitchStatement != 1 { 143 | t.Error("expected 1 switch statement got", result.SwitchStatement) 144 | } 145 | } 146 | 147 | func TestGoStatementCount(t *testing.T) { 148 | parser := Parser{} 149 | result := parser.ParsePackages([]string{"./fixture/..."}, true) 150 | if result.GoStatement != 2 { 151 | t.Error("expected 2 switch statement got", result.GoStatement) 152 | } 153 | } 154 | 155 | func TestPackageCount(t *testing.T) { 156 | parser := Parser{} 157 | result := parser.ParsePackages([]string{"./fixture/..."}, true) 158 | if result.GoStatement != 2 { 159 | t.Error("expected 2 package got", result.Package) 160 | } 161 | } 162 | 163 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "flag" 6 | "go/ast" 7 | "go/parser" 8 | "go/token" 9 | "io" 10 | "log" 11 | "os" 12 | "strings" 13 | 14 | "github.com/kisielk/gotool" 15 | ) 16 | 17 | //Result - container for analysis results 18 | type Result struct { 19 | LOC int 20 | CLOC int 21 | NCLOC int 22 | 23 | Package int 24 | 25 | Struct int 26 | Interface int 27 | 28 | Method int 29 | ExportedMethod int 30 | MethodLOC int 31 | Function int 32 | ExportedFunction int 33 | FunctionLOC int 34 | 35 | Import int 36 | 37 | IfStatement int 38 | SwitchStatement int 39 | GoStatement int 40 | 41 | Test int 42 | Benchmark int 43 | Assertion int 44 | } 45 | 46 | //Parser - Code parser struct 47 | type Parser struct{} 48 | 49 | //ParseDir - Parse all files within directory 50 | func (p *Parser) ParsePackages(packageSpec []string, noVendor bool) *Result { 51 | 52 | res := &Result{} 53 | for _, pac := range gotool.ImportPaths(packageSpec) { 54 | 55 | //create the file set 56 | fset := token.NewFileSet() 57 | 58 | if noVendor && strings.Contains(pac, "/vendor/") { 59 | continue 60 | } 61 | 62 | res.Package++ 63 | 64 | d, err := parser.ParseDir(fset, pac, nil, parser.ParseComments) 65 | if err != nil { 66 | log.Fatal(err) 67 | } 68 | 69 | //count up lines 70 | fset.Iterate(func(file *token.File) bool { 71 | loc, cloc, assertions := p.CountLOC(file.Name()) 72 | res.LOC += loc 73 | res.CLOC += cloc 74 | res.NCLOC += (loc - cloc) 75 | res.Assertion += assertions 76 | return true 77 | }) 78 | 79 | //setup visitors 80 | var visitors []AstVisitor 81 | visitors = append( 82 | visitors, 83 | &TypeVisitor{res: res}, 84 | &FuncVisitor{res: res, fset: fset}, 85 | &ImportVisitor{res: res}, 86 | &FlowControlVisitor{res: res}) 87 | 88 | //count entities 89 | for _, pkg := range d { 90 | ast.Inspect(pkg, func(n ast.Node) bool { 91 | for _, vis := range visitors { 92 | vis.Visit(n) 93 | } 94 | return true 95 | }) 96 | } 97 | } 98 | return res 99 | } 100 | 101 | //CountLOC - count lines of code, pull LOC, Comments, assertions 102 | func (p *Parser) CountLOC(filePath string) (int, int, int) { 103 | 104 | f, err := os.Open(filePath) 105 | if err != nil { 106 | log.Fatal(err) 107 | } 108 | defer f.Close() 109 | 110 | r := bufio.NewReader(f) 111 | 112 | var loc int 113 | var cloc int 114 | var assertions int 115 | var inBlockComment bool 116 | 117 | assertionPrefixes := []string{ 118 | "So(", 119 | "convey.So(", 120 | "assert.", 121 | } 122 | 123 | for { 124 | line, isPrefix, err := r.ReadLine() 125 | if err == io.EOF { 126 | return loc, cloc, assertions 127 | } 128 | if isPrefix == true { 129 | continue //incomplete line 130 | } 131 | if len(line) == 0 { 132 | continue //empty line 133 | } 134 | if strings.Index(strings.TrimSpace(string(line)), "//") == 0 { 135 | cloc++ //slash comment at start of line 136 | continue 137 | } 138 | for _, prefix := range assertionPrefixes { 139 | if strings.HasPrefix(strings.TrimSpace(string(line)), prefix) { 140 | assertions++ 141 | } 142 | } 143 | 144 | blockCommentStartPos := strings.Index(strings.TrimSpace(string(line)), "/*") 145 | blockCommentEndPos := strings.LastIndex(strings.TrimSpace(string(line)), "*/") 146 | 147 | if blockCommentStartPos > -1 { 148 | //block was started and not terminated 149 | if blockCommentEndPos == -1 || blockCommentStartPos > blockCommentEndPos { 150 | inBlockComment = true 151 | } 152 | } 153 | if blockCommentEndPos > -1 { 154 | //end of block is found and no new block was started 155 | if blockCommentStartPos == -1 || blockCommentEndPos > blockCommentStartPos { 156 | inBlockComment = false 157 | cloc++ //end of block counts as a comment line but we're already out of the block 158 | } 159 | } 160 | 161 | loc++ 162 | if inBlockComment { 163 | cloc++ 164 | } 165 | } 166 | } 167 | 168 | func main() { 169 | 170 | //default to current working dir 171 | pwd, err := os.Getwd() 172 | if err != nil { 173 | log.Fatal("Failed to get current working dir: ", err.Error()) 174 | } 175 | 176 | targetDir := flag.String("d", pwd, "target directory") 177 | outputFmt := flag.String("o", "text", "output format") 178 | noVendor := flag.Bool("no-vendor", false, "exclude vendor directory") 179 | flag.Parse() 180 | 181 | var report ReportInterface 182 | switch *outputFmt { 183 | case "text": 184 | report = &TextReport{writer: os.Stdout} 185 | case "json": 186 | report = &JSONReport{writer: os.Stdout} 187 | } 188 | 189 | var args []string 190 | if len(flag.Args()) == 0 { 191 | args = append(args, *targetDir+"/...") 192 | } else { 193 | args = flag.Args() 194 | } 195 | 196 | p := Parser{} 197 | report.Print(p.ParsePackages(args, *noVendor)) 198 | } 199 | --------------------------------------------------------------------------------