mirror of https://github.com/golang/go.git
cmd/go: allow users to specify required fields in JSON output
For #29666 Change-Id: Ibae3d75bb2c19571c8d473cb47d6c4b3a880bba8 Reviewed-on: https://go-review.googlesource.com/c/go/+/381035 Trust: Michael Matloob <matloob@golang.org> Run-TryBot: Michael Matloob <matloob@golang.org> Reviewed-by: Bryan Mills <bcmills@google.com> TryBot-Result: Gopher Robot <gobot@golang.org>
This commit is contained in:
parent
6c6a8fb702
commit
ec4687f337
|
|
@ -869,7 +869,10 @@
|
|||
// for the go/build package's Context type.
|
||||
//
|
||||
// The -json flag causes the package data to be printed in JSON format
|
||||
// instead of using the template format.
|
||||
// instead of using the template format. The JSON flag can optionally be
|
||||
// provided with a set of comma-separated required field names to be output.
|
||||
// If so, those required fields will always appear in JSON output, but
|
||||
// others may be omitted to save work in computing the JSON struct.
|
||||
//
|
||||
// The -compiled flag causes list to set CompiledGoFiles to the Go source
|
||||
// files presented to the compiler. Typically this means that it repeats
|
||||
|
|
|
|||
|
|
@ -13,7 +13,9 @@ import (
|
|||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"reflect"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"text/template"
|
||||
|
||||
|
|
@ -157,7 +159,10 @@ For more information about the meaning of these fields see the documentation
|
|||
for the go/build package's Context type.
|
||||
|
||||
The -json flag causes the package data to be printed in JSON format
|
||||
instead of using the template format.
|
||||
instead of using the template format. The JSON flag can optionally be
|
||||
provided with a set of comma-separated required field names to be output.
|
||||
If so, those required fields will always appear in JSON output, but
|
||||
others may be omitted to save work in computing the JSON struct.
|
||||
|
||||
The -compiled flag causes list to set CompiledGoFiles to the Go source
|
||||
files presented to the compiler. Typically this means that it repeats
|
||||
|
|
@ -316,29 +321,79 @@ For more about modules, see https://golang.org/ref/mod.
|
|||
func init() {
|
||||
CmdList.Run = runList // break init cycle
|
||||
work.AddBuildFlags(CmdList, work.DefaultBuildFlags)
|
||||
CmdList.Flag.Var(&listJsonFields, "json", "")
|
||||
}
|
||||
|
||||
var (
|
||||
listCompiled = CmdList.Flag.Bool("compiled", false, "")
|
||||
listDeps = CmdList.Flag.Bool("deps", false, "")
|
||||
listE = CmdList.Flag.Bool("e", false, "")
|
||||
listExport = CmdList.Flag.Bool("export", false, "")
|
||||
listFmt = CmdList.Flag.String("f", "", "")
|
||||
listFind = CmdList.Flag.Bool("find", false, "")
|
||||
listJson = CmdList.Flag.Bool("json", false, "")
|
||||
listM = CmdList.Flag.Bool("m", false, "")
|
||||
listRetracted = CmdList.Flag.Bool("retracted", false, "")
|
||||
listTest = CmdList.Flag.Bool("test", false, "")
|
||||
listU = CmdList.Flag.Bool("u", false, "")
|
||||
listVersions = CmdList.Flag.Bool("versions", false, "")
|
||||
listCompiled = CmdList.Flag.Bool("compiled", false, "")
|
||||
listDeps = CmdList.Flag.Bool("deps", false, "")
|
||||
listE = CmdList.Flag.Bool("e", false, "")
|
||||
listExport = CmdList.Flag.Bool("export", false, "")
|
||||
listFmt = CmdList.Flag.String("f", "", "")
|
||||
listFind = CmdList.Flag.Bool("find", false, "")
|
||||
listJson bool
|
||||
listJsonFields jsonFlag // If not empty, only output these fields.
|
||||
listM = CmdList.Flag.Bool("m", false, "")
|
||||
listRetracted = CmdList.Flag.Bool("retracted", false, "")
|
||||
listTest = CmdList.Flag.Bool("test", false, "")
|
||||
listU = CmdList.Flag.Bool("u", false, "")
|
||||
listVersions = CmdList.Flag.Bool("versions", false, "")
|
||||
)
|
||||
|
||||
// A StringsFlag is a command-line flag that interprets its argument
|
||||
// as a space-separated list of possibly-quoted strings.
|
||||
type jsonFlag map[string]bool
|
||||
|
||||
func (v *jsonFlag) Set(s string) error {
|
||||
if v, err := strconv.ParseBool(s); err == nil {
|
||||
listJson = v
|
||||
return nil
|
||||
}
|
||||
listJson = true
|
||||
if *v == nil {
|
||||
*v = make(map[string]bool)
|
||||
}
|
||||
for _, f := range strings.Split(s, ",") {
|
||||
(*v)[f] = true
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (v *jsonFlag) String() string {
|
||||
var fields []string
|
||||
for f := range *v {
|
||||
fields = append(fields, f)
|
||||
}
|
||||
sort.Strings(fields)
|
||||
return strings.Join(fields, ",")
|
||||
}
|
||||
|
||||
func (v *jsonFlag) IsBoolFlag() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (v *jsonFlag) needAll() bool {
|
||||
return len(*v) == 0
|
||||
}
|
||||
|
||||
func (v *jsonFlag) needAny(fields ...string) bool {
|
||||
if v.needAll() {
|
||||
return true
|
||||
}
|
||||
for _, f := range fields {
|
||||
if (*v)[f] {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
var nl = []byte{'\n'}
|
||||
|
||||
func runList(ctx context.Context, cmd *base.Command, args []string) {
|
||||
modload.InitWorkfile()
|
||||
|
||||
if *listFmt != "" && *listJson == true {
|
||||
if *listFmt != "" && listJson == true {
|
||||
base.Fatalf("go list -f cannot be used with -json")
|
||||
}
|
||||
|
||||
|
|
@ -357,9 +412,18 @@ func runList(ctx context.Context, cmd *base.Command, args []string) {
|
|||
}
|
||||
}
|
||||
|
||||
var do func(any)
|
||||
if *listJson {
|
||||
var do func(x any)
|
||||
if listJson {
|
||||
do = func(x any) {
|
||||
if !listJsonFields.needAll() {
|
||||
v := reflect.ValueOf(x).Elem() // do is always called with a non-nil pointer.
|
||||
// Clear all non-requested fields.
|
||||
for i := 0; i < v.NumField(); i++ {
|
||||
if !listJsonFields.needAny(v.Type().Field(i).Name) {
|
||||
v.Field(i).Set(reflect.Zero(v.Type().Field(i).Type))
|
||||
}
|
||||
}
|
||||
}
|
||||
b, err := json.MarshalIndent(x, "", "\t")
|
||||
if err != nil {
|
||||
out.Flush()
|
||||
|
|
@ -589,7 +653,7 @@ func runList(ctx context.Context, cmd *base.Command, args []string) {
|
|||
}
|
||||
|
||||
// Do we need to run a build to gather information?
|
||||
needStale := *listJson || strings.Contains(*listFmt, ".Stale")
|
||||
needStale := (listJson && listJsonFields.needAny("Stale", "StaleReason")) || strings.Contains(*listFmt, ".Stale")
|
||||
if needStale || *listExport || *listCompiled {
|
||||
var b work.Builder
|
||||
b.Init()
|
||||
|
|
|
|||
|
|
@ -0,0 +1,52 @@
|
|||
# Test using -json flag to specify specific fields.
|
||||
|
||||
# Test -json produces "full" output by looking for multiple fields present.
|
||||
go list -json .
|
||||
stdout '"Name": "a"'
|
||||
stdout '"Stale": true'
|
||||
# Same thing for -json=true
|
||||
go list -json=true .
|
||||
stdout '"Name": "a"'
|
||||
stdout '"Stale": true'
|
||||
|
||||
# Test -json=false produces non-json output.
|
||||
go list -json=false
|
||||
cmp stdout want-non-json.txt
|
||||
|
||||
# Test -json=<field> keeps only that field.
|
||||
go list -json=Name
|
||||
cmp stdout want-json-name.txt
|
||||
|
||||
# Test -json=<field> with multiple fields.
|
||||
go list -json=ImportPath,Name,GoFiles,Imports
|
||||
cmp stdout want-json-multiple.txt
|
||||
|
||||
-- go.mod --
|
||||
module example.com/a
|
||||
|
||||
go 1.18
|
||||
-- a.go --
|
||||
package a
|
||||
|
||||
import "fmt"
|
||||
|
||||
func F() {
|
||||
fmt.Println("hey there")
|
||||
}
|
||||
-- want-non-json.txt --
|
||||
example.com/a
|
||||
-- want-json-name.txt --
|
||||
{
|
||||
"Name": "a"
|
||||
}
|
||||
-- want-json-multiple.txt --
|
||||
{
|
||||
"ImportPath": "example.com/a",
|
||||
"Name": "a",
|
||||
"GoFiles": [
|
||||
"a.go"
|
||||
],
|
||||
"Imports": [
|
||||
"fmt"
|
||||
]
|
||||
}
|
||||
Loading…
Reference in New Issue