diff --git a/.github/workflows/cli.yml b/.github/workflows/cli.yml index d3f2ecf..ebaef5d 100644 --- a/.github/workflows/cli.yml +++ b/.github/workflows/cli.yml @@ -47,6 +47,9 @@ jobs: - name: test run: go run internal/build/build.go test + - name: test urfave-cli-genflags + run: make -C cmd/urfave-cli-genflags + - name: check-binary-size run: go run internal/build/build.go check-binary-size diff --git a/.gitignore b/.gitignore index cbccea2..d7d26b1 100644 --- a/.gitignore +++ b/.gitignore @@ -9,3 +9,5 @@ coverage.txt internal/*/built-example vendor +/cmd/urfave-cli-genflags/urfave-cli-genflags +*.exe diff --git a/cli.go b/cli.go index e062d79..b3b864c 100644 --- a/cli.go +++ b/cli.go @@ -22,4 +22,4 @@ // } package cli -//go:generate go run internal/genflags/cmd/genflags/main.go +//go:generate go run cmd/urfave-cli-genflags/main.go diff --git a/cmd/urfave-cli-genflags/Makefile b/cmd/urfave-cli-genflags/Makefile new file mode 100644 index 0000000..3b11415 --- /dev/null +++ b/cmd/urfave-cli-genflags/Makefile @@ -0,0 +1,21 @@ +GOTEST_FLAGS ?= -v --coverprofile main.coverprofile --covermode count --cover github.com/urfave/cli/v2/cmd/urfave-cli-genflags +GOBUILD_FLAGS ?= -x + +.PHONY: all +all: test build smoke-test + +.PHONY: test +test: + go test $(GOTEST_FLAGS) ./... + +.PHONY: build +build: + go build $(GOBUILD_FLAGS) ./... + +.PHONY: smoke-test +smoke-test: build + ./urfave-cli-genflags --help + +.PHONY: show-cover +show-cover: + go tool cover -func main.coverprofile diff --git a/cmd/urfave-cli-genflags/README.md b/cmd/urfave-cli-genflags/README.md new file mode 100644 index 0000000..9424234 --- /dev/null +++ b/cmd/urfave-cli-genflags/README.md @@ -0,0 +1,15 @@ +# urfave-cli-genflags + +This is a tool that is used internally by [urfave/cli] to generate +flag types and methods from a YAML input. It intentionally pins +usage of `github.com/urfave/cli/v2` to a *release* rather than +using the adjacent code so that changes don't result in *this* tool +refusing to compile. It's almost like dogfooding? + +## support warning + +This tool is maintained as a sub-project and is not covered by the +API and backward compatibility guaranteed by releases of +[urfave/cli]. + +[urfave/cli]: https://github.com/urfave/cli diff --git a/internal/genflags/generated.gotmpl b/cmd/urfave-cli-genflags/generated.gotmpl similarity index 100% rename from internal/genflags/generated.gotmpl rename to cmd/urfave-cli-genflags/generated.gotmpl diff --git a/internal/genflags/generated_test.gotmpl b/cmd/urfave-cli-genflags/generated_test.gotmpl similarity index 100% rename from internal/genflags/generated_test.gotmpl rename to cmd/urfave-cli-genflags/generated_test.gotmpl diff --git a/cmd/urfave-cli-genflags/go.mod b/cmd/urfave-cli-genflags/go.mod new file mode 100644 index 0000000..af40aaa --- /dev/null +++ b/cmd/urfave-cli-genflags/go.mod @@ -0,0 +1,15 @@ +module github.com/urfave/cli/v2/cmd/urfave-cli-genflags + +go 1.18 + +require ( + github.com/urfave/cli/v2 v2.11.2 + golang.org/x/text v0.3.7 + gopkg.in/yaml.v3 v3.0.1 +) + +require ( + github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect + github.com/russross/blackfriday/v2 v2.1.0 // indirect + github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect +) diff --git a/cmd/urfave-cli-genflags/go.sum b/cmd/urfave-cli-genflags/go.sum new file mode 100644 index 0000000..9821127 --- /dev/null +++ b/cmd/urfave-cli-genflags/go.sum @@ -0,0 +1,17 @@ +github.com/BurntSushi/toml v1.1.0 h1:ksErzDEI1khOiGPgpwuI7x2ebx/uXQNw7xJpn9Eq1+I= +github.com/BurntSushi/toml v1.1.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= +github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w= +github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/urfave/cli/v2 v2.11.2 h1:FVfNg4m3vbjbBpLYxW//WjxUoHvJ9TlppXcqY9Q9ZfA= +github.com/urfave/cli/v2 v2.11.2/go.mod h1:f8iq5LtQ/bLxafbdBSLPPNsgaW0l/2fYYEHhAyPlwvo= +github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU= +github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8= +golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/internal/genflags/cmd/genflags/main.go b/cmd/urfave-cli-genflags/main.go similarity index 53% rename from internal/genflags/cmd/genflags/main.go rename to cmd/urfave-cli-genflags/main.go index 4212e60..835216a 100644 --- a/internal/genflags/cmd/genflags/main.go +++ b/cmd/urfave-cli-genflags/main.go @@ -9,12 +9,14 @@ import ( "os/exec" "os/signal" "path/filepath" + "sort" "strings" "syscall" "text/template" "github.com/urfave/cli/v2" - "github.com/urfave/cli/v2/internal/genflags" + "golang.org/x/text/cases" + "golang.org/x/text/language" "gopkg.in/yaml.v3" ) @@ -22,6 +24,16 @@ const ( defaultPackageName = "cli" ) +var ( + //go:embed generated.gotmpl + TemplateString string + + //go:embed generated_test.gotmpl + TestTemplateString string + + titler = cases.Title(language.Und, cases.NoLower) +) + func sh(ctx context.Context, exe string, args ...string) (string, error) { cmd := exec.CommandContext(ctx, exe, args...) cmd.Stderr = os.Stderr @@ -92,7 +104,7 @@ func runGenFlags(cCtx *cli.Context) error { return err } - spec := &genflags.Spec{} + spec := &Spec{} if err := yaml.Unmarshal(specBytes, spec); err != nil { return err } @@ -123,12 +135,12 @@ func runGenFlags(cCtx *cli.Context) error { spec.UrfaveCLITestNamespace = "cli." } - genTmpl, err := template.New("gen").Parse(genflags.TemplateString) + genTmpl, err := template.New("gen").Parse(TemplateString) if err != nil { return err } - genTestTmpl, err := template.New("gen_test").Parse(genflags.TestTemplateString) + genTestTmpl, err := template.New("gen_test").Parse(TestTemplateString) if err != nil { return err } @@ -161,3 +173,120 @@ func runGenFlags(cCtx *cli.Context) error { return nil } + +func TypeName(goType string, fc *FlagTypeConfig) string { + if fc != nil && strings.TrimSpace(fc.TypeName) != "" { + return strings.TrimSpace(fc.TypeName) + } + + dotSplit := strings.Split(goType, ".") + goType = dotSplit[len(dotSplit)-1] + + if strings.HasPrefix(goType, "[]") { + return titler.String(strings.TrimPrefix(goType, "[]")) + "SliceFlag" + } + + return titler.String(goType) + "Flag" +} + +type Spec struct { + FlagTypes map[string]*FlagTypeConfig `yaml:"flag_types"` + PackageName string `yaml:"package_name"` + TestPackageName string `yaml:"test_package_name"` + UrfaveCLINamespace string `yaml:"urfave_cli_namespace"` + UrfaveCLITestNamespace string `yaml:"urfave_cli_test_namespace"` +} + +func (gfs *Spec) SortedFlagTypes() []*FlagType { + typeNames := []string{} + + for name := range gfs.FlagTypes { + if strings.HasPrefix(name, "[]") { + name = strings.TrimPrefix(name, "[]") + "Slice" + } + + typeNames = append(typeNames, name) + } + + sort.Strings(typeNames) + + ret := make([]*FlagType, len(typeNames)) + + for i, typeName := range typeNames { + ret[i] = &FlagType{ + GoType: typeName, + Config: gfs.FlagTypes[typeName], + } + } + + return ret +} + +type FlagTypeConfig struct { + SkipInterfaces []string `yaml:"skip_interfaces"` + StructFields []*FlagStructField `yaml:"struct_fields"` + TypeName string `yaml:"type_name"` + ValuePointer bool `yaml:"value_pointer"` +} + +type FlagStructField struct { + Name string + Type string +} + +type FlagType struct { + GoType string + Config *FlagTypeConfig +} + +func (ft *FlagType) StructFields() []*FlagStructField { + if ft.Config == nil || ft.Config.StructFields == nil { + return []*FlagStructField{} + } + + return ft.Config.StructFields +} + +func (ft *FlagType) ValuePointer() bool { + if ft.Config == nil { + return false + } + + return ft.Config.ValuePointer +} + +func (ft *FlagType) TypeName() string { + return TypeName(ft.GoType, ft.Config) +} + +func (ft *FlagType) GenerateFmtStringerInterface() bool { + return ft.skipInterfaceNamed("fmt.Stringer") +} + +func (ft *FlagType) GenerateFlagInterface() bool { + return ft.skipInterfaceNamed("Flag") +} + +func (ft *FlagType) GenerateRequiredFlagInterface() bool { + return ft.skipInterfaceNamed("RequiredFlag") +} + +func (ft *FlagType) GenerateVisibleFlagInterface() bool { + return ft.skipInterfaceNamed("VisibleFlag") +} + +func (ft *FlagType) skipInterfaceNamed(name string) bool { + if ft.Config == nil { + return true + } + + lowName := strings.ToLower(name) + + for _, interfaceName := range ft.Config.SkipInterfaces { + if strings.ToLower(interfaceName) == lowName { + return false + } + } + + return true +} diff --git a/internal/genflags/spec_test.go b/cmd/urfave-cli-genflags/main_test.go similarity index 52% rename from internal/genflags/spec_test.go rename to cmd/urfave-cli-genflags/main_test.go index 25a9c8b..b5c9fee 100644 --- a/internal/genflags/spec_test.go +++ b/cmd/urfave-cli-genflags/main_test.go @@ -1,29 +1,63 @@ -package genflags_test +package main_test import ( + "fmt" "reflect" "testing" - "github.com/urfave/cli/v2/internal/genflags" + main "github.com/urfave/cli/v2/cmd/urfave-cli-genflags" ) +func TestTypeName(t *testing.T) { + for _, tc := range []struct { + gt string + fc *main.FlagTypeConfig + expected string + }{ + {gt: "int", fc: nil, expected: "IntFlag"}, + {gt: "int", fc: &main.FlagTypeConfig{}, expected: "IntFlag"}, + {gt: "int", fc: &main.FlagTypeConfig{TypeName: "VeryIntyFlag"}, expected: "VeryIntyFlag"}, + {gt: "[]bool", fc: nil, expected: "BoolSliceFlag"}, + {gt: "[]bool", fc: &main.FlagTypeConfig{}, expected: "BoolSliceFlag"}, + {gt: "[]bool", fc: &main.FlagTypeConfig{TypeName: "ManyTruthsFlag"}, expected: "ManyTruthsFlag"}, + {gt: "time.Rumination", fc: nil, expected: "RuminationFlag"}, + {gt: "time.Rumination", fc: &main.FlagTypeConfig{}, expected: "RuminationFlag"}, + {gt: "time.Rumination", fc: &main.FlagTypeConfig{TypeName: "PonderFlag"}, expected: "PonderFlag"}, + } { + t.Run( + fmt.Sprintf("type=%s,cfg=%v", tc.gt, func() string { + if tc.fc != nil { + return tc.fc.TypeName + } + return "nil" + }()), + func(ct *testing.T) { + actual := main.TypeName(tc.gt, tc.fc) + if tc.expected != actual { + ct.Errorf("expected %q, got %q", tc.expected, actual) + } + }, + ) + } +} + func TestSpec_SortedFlagTypes(t *testing.T) { - spec := &genflags.Spec{ - FlagTypes: map[string]*genflags.FlagTypeConfig{ - "nerf": &genflags.FlagTypeConfig{}, + spec := &main.Spec{ + FlagTypes: map[string]*main.FlagTypeConfig{ + "nerf": &main.FlagTypeConfig{}, "gerf": nil, }, } actual := spec.SortedFlagTypes() - expected := []*genflags.FlagType{ + expected := []*main.FlagType{ { GoType: "gerf", Config: nil, }, { GoType: "nerf", - Config: &genflags.FlagTypeConfig{}, + Config: &main.FlagTypeConfig{}, }, } if !reflect.DeepEqual(expected, actual) { @@ -31,12 +65,12 @@ func TestSpec_SortedFlagTypes(t *testing.T) { } } -func genFlagType() *genflags.FlagType { - return &genflags.FlagType{ +func genFlagType() *main.FlagType { + return &main.FlagType{ GoType: "blerf", - Config: &genflags.FlagTypeConfig{ + Config: &main.FlagTypeConfig{ SkipInterfaces: []string{"fmt.Stringer"}, - StructFields: []*genflags.FlagStructField{ + StructFields: []*main.FlagStructField{ { Name: "Foibles", Type: "int", diff --git a/docs/CONTRIBUTING.md b/docs/CONTRIBUTING.md index b0d9bfc..4462899 100644 --- a/docs/CONTRIBUTING.md +++ b/docs/CONTRIBUTING.md @@ -95,7 +95,7 @@ The built-in `go generate` command is used to run the commands specified in line help system which may be consulted for further information, e.g.: ```sh -go run internal/genflags/cmd/genflags/main.go --help +go run cmd/urfave-cli-genflags/main.go --help ``` #### docs output diff --git a/flag-spec.yaml b/flag-spec.yaml index 45f054d..b1a0735 100644 --- a/flag-spec.yaml +++ b/flag-spec.yaml @@ -1,6 +1,6 @@ # NOTE: this file is used by the tool defined in -# ./internal/genflags/cmd/genflags/main.go which uses the -# `genflags.Spec` type that maps to this file structure. +# ./cmd/urfave-cli-genflags/main.go which uses the +# `Spec` type that maps to this file structure. flag_types: bool: {} diff --git a/flag.go b/flag.go index dbed577..37c9fda 100644 --- a/flag.go +++ b/flag.go @@ -7,7 +7,6 @@ import ( "io/ioutil" "regexp" "runtime" - "strconv" "strings" "syscall" "time" @@ -318,53 +317,6 @@ func stringifyFlag(f Flag) string { fmt.Sprintf("%s\t%s", prefixedNames(df.Names(), placeholder), usageWithDefault)) } -func stringifyIntSliceFlag(f *IntSliceFlag) string { - var defaultVals []string - if f.Value != nil && len(f.Value.Value()) > 0 { - for _, i := range f.Value.Value() { - defaultVals = append(defaultVals, strconv.Itoa(i)) - } - } - - return stringifySliceFlag(f.Usage, f.Names(), defaultVals) -} - -func stringifyInt64SliceFlag(f *Int64SliceFlag) string { - var defaultVals []string - if f.Value != nil && len(f.Value.Value()) > 0 { - for _, i := range f.Value.Value() { - defaultVals = append(defaultVals, strconv.FormatInt(i, 10)) - } - } - - return stringifySliceFlag(f.Usage, f.Names(), defaultVals) -} - -func stringifyFloat64SliceFlag(f *Float64SliceFlag) string { - var defaultVals []string - - if f.Value != nil && len(f.Value.Value()) > 0 { - for _, i := range f.Value.Value() { - defaultVals = append(defaultVals, strings.TrimRight(strings.TrimRight(fmt.Sprintf("%f", i), "0"), ".")) - } - } - - return stringifySliceFlag(f.Usage, f.Names(), defaultVals) -} - -func stringifyStringSliceFlag(f *StringSliceFlag) string { - var defaultVals []string - if f.Value != nil && len(f.Value.Value()) > 0 { - for _, s := range f.Value.Value() { - if len(s) > 0 { - defaultVals = append(defaultVals, strconv.Quote(s)) - } - } - } - - return stringifySliceFlag(f.Usage, f.Names(), defaultVals) -} - func stringifySliceFlag(usage string, names, defaultVals []string) string { placeholder, usage := unquoteUsage(usage) if placeholder == "" { @@ -377,11 +329,8 @@ func stringifySliceFlag(usage string, names, defaultVals []string) string { } usageWithDefault := strings.TrimSpace(fmt.Sprintf("%s%s", usage, defaultVal)) - multiInputString := "(accepts multiple inputs)" - if usageWithDefault != "" { - multiInputString = "\t" + multiInputString - } - return fmt.Sprintf("%s\t%s%s", prefixedNames(names, placeholder), usageWithDefault, multiInputString) + pn := prefixedNames(names, placeholder) + return fmt.Sprintf("%s [ %s ]\t%s", pn, pn, usageWithDefault) } func hasFlag(flags []Flag, fl Flag) bool { diff --git a/flag_float64_slice.go b/flag_float64_slice.go index 031ec1d..e4aff73 100644 --- a/flag_float64_slice.go +++ b/flag_float64_slice.go @@ -83,7 +83,7 @@ func (f *Float64Slice) Get() interface{} { // String returns a readable representation of this value // (for usage defaults) func (f *Float64SliceFlag) String() string { - return withEnvHint(f.GetEnvVars(), stringifyFloat64SliceFlag(f)) + return withEnvHint(f.GetEnvVars(), f.stringify()) } // TakesValue returns true if the flag takes a value, otherwise false @@ -169,6 +169,18 @@ func (f *Float64SliceFlag) Get(ctx *Context) []float64 { return ctx.Float64Slice(f.Name) } +func (f *Float64SliceFlag) stringify() string { + var defaultVals []string + + if f.Value != nil && len(f.Value.Value()) > 0 { + for _, i := range f.Value.Value() { + defaultVals = append(defaultVals, strings.TrimRight(strings.TrimRight(fmt.Sprintf("%f", i), "0"), ".")) + } + } + + return stringifySliceFlag(f.Usage, f.Names(), defaultVals) +} + // Float64Slice looks up the value of a local Float64SliceFlag, returns // nil if not found func (cCtx *Context) Float64Slice(name string) []float64 { diff --git a/flag_int64_slice.go b/flag_int64_slice.go index 657aaaa..ead4e77 100644 --- a/flag_int64_slice.go +++ b/flag_int64_slice.go @@ -84,7 +84,7 @@ func (i *Int64Slice) Get() interface{} { // String returns a readable representation of this value // (for usage defaults) func (f *Int64SliceFlag) String() string { - return withEnvHint(f.GetEnvVars(), stringifyInt64SliceFlag(f)) + return withEnvHint(f.GetEnvVars(), f.stringify()) } // TakesValue returns true of the flag takes a value, otherwise false @@ -168,6 +168,17 @@ func (f *Int64SliceFlag) Get(ctx *Context) []int64 { return ctx.Int64Slice(f.Name) } +func (f *Int64SliceFlag) stringify() string { + var defaultVals []string + if f.Value != nil && len(f.Value.Value()) > 0 { + for _, i := range f.Value.Value() { + defaultVals = append(defaultVals, strconv.FormatInt(i, 10)) + } + } + + return stringifySliceFlag(f.Usage, f.Names(), defaultVals) +} + // Int64Slice looks up the value of a local Int64SliceFlag, returns // nil if not found func (cCtx *Context) Int64Slice(name string) []int64 { diff --git a/flag_int_slice.go b/flag_int_slice.go index 7c38393..b40e0d8 100644 --- a/flag_int_slice.go +++ b/flag_int_slice.go @@ -95,7 +95,7 @@ func (i *IntSlice) Get() interface{} { // String returns a readable representation of this value // (for usage defaults) func (f *IntSliceFlag) String() string { - return withEnvHint(f.GetEnvVars(), stringifyIntSliceFlag(f)) + return withEnvHint(f.GetEnvVars(), f.stringify()) } // TakesValue returns true of the flag takes a value, otherwise false @@ -179,6 +179,17 @@ func (f *IntSliceFlag) Get(ctx *Context) []int { return ctx.IntSlice(f.Name) } +func (f *IntSliceFlag) stringify() string { + var defaultVals []string + if f.Value != nil && len(f.Value.Value()) > 0 { + for _, i := range f.Value.Value() { + defaultVals = append(defaultVals, strconv.Itoa(i)) + } + } + + return stringifySliceFlag(f.Usage, f.Names(), defaultVals) +} + // IntSlice looks up the value of a local IntSliceFlag, returns // nil if not found func (cCtx *Context) IntSlice(name string) []int { diff --git a/flag_string_slice.go b/flag_string_slice.go index bcdfd4c..9d69342 100644 --- a/flag_string_slice.go +++ b/flag_string_slice.go @@ -4,6 +4,7 @@ import ( "encoding/json" "flag" "fmt" + "strconv" "strings" ) @@ -73,7 +74,7 @@ func (s *StringSlice) Get() interface{} { // String returns a readable representation of this value // (for usage defaults) func (f *StringSliceFlag) String() string { - return withEnvHint(f.GetEnvVars(), stringifyStringSliceFlag(f)) + return withEnvHint(f.GetEnvVars(), f.stringify()) } // TakesValue returns true of the flag takes a value, otherwise false @@ -157,6 +158,19 @@ func (f *StringSliceFlag) Get(ctx *Context) []string { return ctx.StringSlice(f.Name) } +func (f *StringSliceFlag) stringify() string { + var defaultVals []string + if f.Value != nil && len(f.Value.Value()) > 0 { + for _, s := range f.Value.Value() { + if len(s) > 0 { + defaultVals = append(defaultVals, strconv.Quote(s)) + } + } + } + + return stringifySliceFlag(f.Usage, f.Names(), defaultVals) +} + // StringSlice looks up the value of a local StringSliceFlag, returns // nil if not found func (cCtx *Context) StringSlice(name string) []string { diff --git a/flag_test.go b/flag_test.go index e46b1ef..d3ded72 100644 --- a/flag_test.go +++ b/flag_test.go @@ -558,11 +558,11 @@ var stringSliceFlagTests = []struct { value *StringSlice expected string }{ - {"foo", nil, NewStringSlice(""), "--foo value\t(accepts multiple inputs)"}, - {"f", nil, NewStringSlice(""), "-f value\t(accepts multiple inputs)"}, - {"f", nil, NewStringSlice("Lipstick"), "-f value\t(default: \"Lipstick\")\t(accepts multiple inputs)"}, - {"test", nil, NewStringSlice("Something"), "--test value\t(default: \"Something\")\t(accepts multiple inputs)"}, - {"dee", []string{"d"}, NewStringSlice("Inka", "Dinka", "dooo"), "--dee value, -d value\t(default: \"Inka\", \"Dinka\", \"dooo\")\t(accepts multiple inputs)"}, + {"foo", nil, NewStringSlice(""), "--foo value [ --foo value ]\t"}, + {"f", nil, NewStringSlice(""), "-f value [ -f value ]\t"}, + {"f", nil, NewStringSlice("Lipstick"), "-f value [ -f value ]\t(default: \"Lipstick\")"}, + {"test", nil, NewStringSlice("Something"), "--test value [ --test value ]\t(default: \"Something\")"}, + {"dee", []string{"d"}, NewStringSlice("Inka", "Dinka", "dooo"), "--dee value, -d value [ --dee value, -d value ]\t(default: \"Inka\", \"Dinka\", \"dooo\")"}, } func TestStringSliceFlagHelpOutput(t *testing.T) { @@ -911,9 +911,9 @@ var intSliceFlagTests = []struct { value *IntSlice expected string }{ - {"heads", nil, NewIntSlice(), "--heads value\t(accepts multiple inputs)"}, - {"H", nil, NewIntSlice(), "-H value\t(accepts multiple inputs)"}, - {"H", []string{"heads"}, NewIntSlice(9, 3), "-H value, --heads value\t(default: 9, 3)\t(accepts multiple inputs)"}, + {"heads", nil, NewIntSlice(), "--heads value [ --heads value ]\t"}, + {"H", nil, NewIntSlice(), "-H value [ -H value ]\t"}, + {"H", []string{"heads"}, NewIntSlice(9, 3), "-H value, --heads value [ -H value, --heads value ]\t(default: 9, 3)"}, } func TestIntSliceFlagHelpOutput(t *testing.T) { @@ -1008,10 +1008,10 @@ var int64SliceFlagTests = []struct { value *Int64Slice expected string }{ - {"heads", nil, NewInt64Slice(), "--heads value\t(accepts multiple inputs)"}, - {"H", nil, NewInt64Slice(), "-H value\t(accepts multiple inputs)"}, + {"heads", nil, NewInt64Slice(), "--heads value [ --heads value ]\t"}, + {"H", nil, NewInt64Slice(), "-H value [ -H value ]\t"}, {"heads", []string{"H"}, NewInt64Slice(int64(2), int64(17179869184)), - "--heads value, -H value\t(default: 2, 17179869184)\t(accepts multiple inputs)"}, + "--heads value, -H value [ --heads value, -H value ]\t(default: 2, 17179869184)"}, } func TestInt64SliceFlagHelpOutput(t *testing.T) { @@ -1169,10 +1169,10 @@ var float64SliceFlagTests = []struct { value *Float64Slice expected string }{ - {"heads", nil, NewFloat64Slice(), "--heads value\t(accepts multiple inputs)"}, - {"H", nil, NewFloat64Slice(), "-H value\t(accepts multiple inputs)"}, + {"heads", nil, NewFloat64Slice(), "--heads value [ --heads value ]\t"}, + {"H", nil, NewFloat64Slice(), "-H value [ -H value ]\t"}, {"heads", []string{"H"}, NewFloat64Slice(0.1234, -10.5), - "--heads value, -H value\t(default: 0.1234, -10.5)\t(accepts multiple inputs)"}, + "--heads value, -H value [ --heads value, -H value ]\t(default: 0.1234, -10.5)"}, } func TestFloat64SliceFlagHelpOutput(t *testing.T) { @@ -2398,25 +2398,25 @@ func TestFlagDefaultValue(t *testing.T) { name: "stringSclice", flag: &StringSliceFlag{Name: "flag", Value: NewStringSlice("default1", "default2")}, toParse: []string{"--flag", "parsed"}, - expect: `--flag value (default: "default1", "default2") (accepts multiple inputs)`, + expect: `--flag value [ --flag value ] (default: "default1", "default2")`, }, { name: "float64Sclice", flag: &Float64SliceFlag{Name: "flag", Value: NewFloat64Slice(1.1, 2.2)}, toParse: []string{"--flag", "13.3"}, - expect: `--flag value (default: 1.1, 2.2) (accepts multiple inputs)`, + expect: `--flag value [ --flag value ] (default: 1.1, 2.2)`, }, { name: "int64Sclice", flag: &Int64SliceFlag{Name: "flag", Value: NewInt64Slice(1, 2)}, toParse: []string{"--flag", "13"}, - expect: `--flag value (default: 1, 2) (accepts multiple inputs)`, + expect: `--flag value [ --flag value ] (default: 1, 2)`, }, { name: "intSclice", flag: &IntSliceFlag{Name: "flag", Value: NewIntSlice(1, 2)}, toParse: []string{"--flag", "13"}, - expect: `--flag value (default: 1, 2) (accepts multiple inputs)`, + expect: `--flag value [ --flag value ] (default: 1, 2)`, }, { name: "string", diff --git a/go.mod b/go.mod index 4e39308..7fa4542 100644 --- a/go.mod +++ b/go.mod @@ -6,7 +6,6 @@ require ( github.com/BurntSushi/toml v1.1.0 github.com/cpuguy83/go-md2man/v2 v2.0.2 github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 - golang.org/x/text v0.3.7 gopkg.in/yaml.v3 v3.0.1 ) diff --git a/go.sum b/go.sum index 6beae99..0756e41 100644 --- a/go.sum +++ b/go.sum @@ -1,17 +1,11 @@ github.com/BurntSushi/toml v1.1.0 h1:ksErzDEI1khOiGPgpwuI7x2ebx/uXQNw7xJpn9Eq1+I= github.com/BurntSushi/toml v1.1.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= -github.com/cpuguy83/go-md2man/v2 v2.0.1 h1:r/myEWzV9lfsM1tFLgDyu0atFtJ1fXn261LKYj/3DxU= -github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w= github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU= github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8= -golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= -golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e h1:FDhOuMEY4JVRztM/gsbk+IKUQ8kj74bxZrgw87eMMVc= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= diff --git a/internal/build/build.go b/internal/build/build.go index d94dae7..51b560c 100644 --- a/internal/build/build.go +++ b/internal/build/build.go @@ -94,7 +94,7 @@ func main() { }, &cli.StringSliceFlag{ Name: "packages", - Value: cli.NewStringSlice("cli", "altsrc", "internal/build", "internal/genflags"), + Value: cli.NewStringSlice("cli", "altsrc", "internal/build"), }, } diff --git a/internal/genflags/package.go b/internal/genflags/package.go deleted file mode 100644 index 4e5de41..0000000 --- a/internal/genflags/package.go +++ /dev/null @@ -1,34 +0,0 @@ -package genflags - -import ( - _ "embed" - "strings" - - "golang.org/x/text/cases" - "golang.org/x/text/language" -) - -var ( - //go:embed generated.gotmpl - TemplateString string - - //go:embed generated_test.gotmpl - TestTemplateString string - - titler = cases.Title(language.Und, cases.NoLower) -) - -func TypeName(goType string, fc *FlagTypeConfig) string { - if fc != nil && strings.TrimSpace(fc.TypeName) != "" { - return strings.TrimSpace(fc.TypeName) - } - - dotSplit := strings.Split(goType, ".") - goType = dotSplit[len(dotSplit)-1] - - if strings.HasPrefix(goType, "[]") { - return titler.String(strings.TrimPrefix(goType, "[]")) + "SliceFlag" - } - - return titler.String(goType) + "Flag" -} diff --git a/internal/genflags/package_test.go b/internal/genflags/package_test.go deleted file mode 100644 index 3920540..0000000 --- a/internal/genflags/package_test.go +++ /dev/null @@ -1,41 +0,0 @@ -package genflags_test - -import ( - "fmt" - "testing" - - "github.com/urfave/cli/v2/internal/genflags" -) - -func TestTypeName(t *testing.T) { - for _, tc := range []struct { - gt string - fc *genflags.FlagTypeConfig - expected string - }{ - {gt: "int", fc: nil, expected: "IntFlag"}, - {gt: "int", fc: &genflags.FlagTypeConfig{}, expected: "IntFlag"}, - {gt: "int", fc: &genflags.FlagTypeConfig{TypeName: "VeryIntyFlag"}, expected: "VeryIntyFlag"}, - {gt: "[]bool", fc: nil, expected: "BoolSliceFlag"}, - {gt: "[]bool", fc: &genflags.FlagTypeConfig{}, expected: "BoolSliceFlag"}, - {gt: "[]bool", fc: &genflags.FlagTypeConfig{TypeName: "ManyTruthsFlag"}, expected: "ManyTruthsFlag"}, - {gt: "time.Rumination", fc: nil, expected: "RuminationFlag"}, - {gt: "time.Rumination", fc: &genflags.FlagTypeConfig{}, expected: "RuminationFlag"}, - {gt: "time.Rumination", fc: &genflags.FlagTypeConfig{TypeName: "PonderFlag"}, expected: "PonderFlag"}, - } { - t.Run( - fmt.Sprintf("type=%s,cfg=%v", tc.gt, func() string { - if tc.fc != nil { - return tc.fc.TypeName - } - return "nil" - }()), - func(ct *testing.T) { - actual := genflags.TypeName(tc.gt, tc.fc) - if tc.expected != actual { - ct.Errorf("expected %q, got %q", tc.expected, actual) - } - }, - ) - } -} diff --git a/internal/genflags/spec.go b/internal/genflags/spec.go deleted file mode 100644 index a7afb22..0000000 --- a/internal/genflags/spec.go +++ /dev/null @@ -1,108 +0,0 @@ -package genflags - -import ( - "sort" - "strings" -) - -type Spec struct { - FlagTypes map[string]*FlagTypeConfig `yaml:"flag_types"` - PackageName string `yaml:"package_name"` - TestPackageName string `yaml:"test_package_name"` - UrfaveCLINamespace string `yaml:"urfave_cli_namespace"` - UrfaveCLITestNamespace string `yaml:"urfave_cli_test_namespace"` -} - -func (gfs *Spec) SortedFlagTypes() []*FlagType { - typeNames := []string{} - - for name := range gfs.FlagTypes { - if strings.HasPrefix(name, "[]") { - name = strings.TrimPrefix(name, "[]") + "Slice" - } - - typeNames = append(typeNames, name) - } - - sort.Strings(typeNames) - - ret := make([]*FlagType, len(typeNames)) - - for i, typeName := range typeNames { - ret[i] = &FlagType{ - GoType: typeName, - Config: gfs.FlagTypes[typeName], - } - } - - return ret -} - -type FlagTypeConfig struct { - SkipInterfaces []string `yaml:"skip_interfaces"` - StructFields []*FlagStructField `yaml:"struct_fields"` - TypeName string `yaml:"type_name"` - ValuePointer bool `yaml:"value_pointer"` -} - -type FlagStructField struct { - Name string - Type string -} - -type FlagType struct { - GoType string - Config *FlagTypeConfig -} - -func (ft *FlagType) StructFields() []*FlagStructField { - if ft.Config == nil || ft.Config.StructFields == nil { - return []*FlagStructField{} - } - - return ft.Config.StructFields -} - -func (ft *FlagType) ValuePointer() bool { - if ft.Config == nil { - return false - } - - return ft.Config.ValuePointer -} - -func (ft *FlagType) TypeName() string { - return TypeName(ft.GoType, ft.Config) -} - -func (ft *FlagType) GenerateFmtStringerInterface() bool { - return ft.skipInterfaceNamed("fmt.Stringer") -} - -func (ft *FlagType) GenerateFlagInterface() bool { - return ft.skipInterfaceNamed("Flag") -} - -func (ft *FlagType) GenerateRequiredFlagInterface() bool { - return ft.skipInterfaceNamed("RequiredFlag") -} - -func (ft *FlagType) GenerateVisibleFlagInterface() bool { - return ft.skipInterfaceNamed("VisibleFlag") -} - -func (ft *FlagType) skipInterfaceNamed(name string) bool { - if ft.Config == nil { - return true - } - - lowName := strings.ToLower(name) - - for _, interfaceName := range ft.Config.SkipInterfaces { - if strings.ToLower(interfaceName) == lowName { - return false - } - } - - return true -}