Skip to content

Commit

Permalink
Porting #1451 to v3
Browse files Browse the repository at this point in the history
  • Loading branch information
meatballhat committed Sep 30, 2022
1 parent 3df286b commit c0a0f27
Show file tree
Hide file tree
Showing 19 changed files with 255 additions and 207 deletions.
3 changes: 3 additions & 0 deletions .github/workflows/cli.yml
Expand Up @@ -46,6 +46,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

Expand Down
1 change: 1 addition & 0 deletions .gitignore
Expand Up @@ -6,5 +6,6 @@ internal/*/built-example
coverage.txt
/.local/
/site/
/cmd/urfave-cli-genflags/urfave-cli-genflags

*.exe
2 changes: 1 addition & 1 deletion cli.go
Expand Up @@ -20,4 +20,4 @@
// }
package cli

//go:generate go run internal/genflags/cmd/genflags/main.go
//go:generate go run cmd/urfave-cli-genflags/main.go
21 changes: 21 additions & 0 deletions 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
15 changes: 15 additions & 0 deletions 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
File renamed without changes.
File renamed without changes.
15 changes: 15 additions & 0 deletions 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
)
17 changes: 17 additions & 0 deletions 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=
Expand Up @@ -9,19 +9,31 @@ 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"
)

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
Expand Down Expand Up @@ -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
}
Expand Down Expand Up @@ -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
}
Expand Down Expand Up @@ -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
}
@@ -1,42 +1,76 @@
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) {
t.Errorf("expected %#v, got %#v", expected, actual)
}
}

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",
Expand Down
2 changes: 1 addition & 1 deletion docs/CONTRIBUTING.md
Expand Up @@ -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
Expand Down
4 changes: 2 additions & 2 deletions 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:
Expand Down
1 change: 0 additions & 1 deletion go.mod
Expand Up @@ -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
)

Expand Down

0 comments on commit c0a0f27

Please sign in to comment.