From 41182b029c6a536f917139a7e27be08cc3a50d23 Mon Sep 17 00:00:00 2001 From: Eyal Posener Date: Sat, 9 May 2020 13:36:38 +0300 Subject: [PATCH] Autogenerate compflag code --- compflag/compflag.go | 182 ++---------------------- compflag/flags.go | 259 +++++++++++++++++++++++++++++++++++ compflag/gen/flags.go.gotmpl | 83 +++++++++++ compflag/gen/main.go | 67 +++++++++ 4 files changed, 419 insertions(+), 172 deletions(-) create mode 100644 compflag/flags.go create mode 100644 compflag/gen/flags.go.gotmpl create mode 100644 compflag/gen/main.go diff --git a/compflag/compflag.go b/compflag/compflag.go index fb1e852..2048c74 100644 --- a/compflag/compflag.go +++ b/compflag/compflag.go @@ -36,13 +36,11 @@ package compflag import ( "flag" - "fmt" "os" "strconv" "time" "github.com/posener/complete/v2" - "github.com/posener/complete/v2/predict" ) // FlagSet is bash completion enabled flag.FlagSet. @@ -69,30 +67,6 @@ func (fs *FlagSet) Complete() { complete.Complete(fs.Name(), complete.FlagSet((*flag.FlagSet)(CommandLine))) } -func (fs *FlagSet) String(name string, value string, usage string, options ...predict.Option) *string { - p := new(string) - (*flag.FlagSet)(fs).Var(newStringValue(value, p, predict.Options(options...)), name, usage) - return p -} - -func (fs *FlagSet) Bool(name string, value bool, usage string, options ...predict.Option) *bool { - p := new(bool) - (*flag.FlagSet)(fs).Var(newBoolValue(value, p, predict.Options(options...)), name, usage) - return p -} - -func (fs *FlagSet) Int(name string, value int, usage string, options ...predict.Option) *int { - p := new(int) - (*flag.FlagSet)(fs).Var(newIntValue(value, p, predict.Options(options...)), name, usage) - return p -} - -func (fs *FlagSet) Duration(name string, value time.Duration, usage string, options ...predict.Option) *time.Duration { - p := new(time.Duration) - (*flag.FlagSet)(fs).Var(newDurationValue(value, p, predict.Options(options...)), name, usage) - return p -} - var CommandLine = (*FlagSet)(flag.CommandLine) // Parse parses command line arguments. It also performs bash completion when needed. @@ -101,164 +75,28 @@ func Parse() { CommandLine.Parse(os.Args[1:]) } -func String(name string, value string, usage string, options ...predict.Option) *string { - return CommandLine.String(name, value, usage, options...) -} - -func Bool(name string, value bool, usage string, options ...predict.Option) *bool { - return CommandLine.Bool(name, value, usage, options...) -} - -func Int(name string, value int, usage string, options ...predict.Option) *int { - return CommandLine.Int(name, value, usage, options...) -} - -func Duration(name string, value time.Duration, usage string, options ...predict.Option) *time.Duration { - return CommandLine.Duration(name, value, usage, options...) -} - -type boolValue struct { - v *bool - predict.Config -} - -func newBoolValue(val bool, p *bool, c predict.Config) *boolValue { - *p = val - return &boolValue{v: p, Config: c} -} - -func (b *boolValue) Set(val string) error { - v, err := strconv.ParseBool(val) - *b.v = v - if err != nil { - return fmt.Errorf("bad value for bool flag") - } - return b.Check(val) -} - -func (b *boolValue) Get() interface{} { return *b.v } - -func (b *boolValue) String() string { - if b == nil || b.v == nil { - return strconv.FormatBool(false) - } - return strconv.FormatBool(*b.v) -} - -func (b *boolValue) IsBoolFlag() bool { return true } - -func (b *boolValue) Predict(prefix string) []string { - if b.Predictor != nil { - return b.Predictor.Predict(prefix) - } +func predictBool(value bool, prefix string) []string { // If false, typing the bool flag is expected to turn it on, so there is nothing to complete // after the flag. - if !*b.v { + if !value { return nil } // Otherwise, suggest only to turn it off. return []string{"false"} } -type stringValue struct { - v *string - predict.Config -} +func parseString(s string) (string, error) { return s, nil } -func newStringValue(val string, p *string, c predict.Config) *stringValue { - *p = val - return &stringValue{v: p, Config: c} -} +func formatString(v string) string { return v } -func (s *stringValue) Set(val string) error { - *s.v = val - return s.Check(val) -} +func parseInt(s string) (int, error) { return strconv.Atoi(s) } -func (s *stringValue) Get() interface{} { - return *s.v -} +func formatInt(v int) string { return strconv.Itoa(v) } -func (s *stringValue) String() string { - if s == nil || s.v == nil { - return "" - } - return *s.v -} +func parseBool(s string) (bool, error) { return strconv.ParseBool(s) } -func (s *stringValue) Predict(prefix string) []string { - if s.Predictor != nil { - return s.Predictor.Predict(prefix) - } - return []string{""} -} +func formatBool(v bool) string { return strconv.FormatBool(v) } -type intValue struct { - v *int - predict.Config -} +func parseDuration(s string) (time.Duration, error) { return time.ParseDuration(s) } -func newIntValue(val int, p *int, c predict.Config) *intValue { - *p = val - return &intValue{v: p, Config: c} -} - -func (i *intValue) Set(val string) error { - v, err := strconv.ParseInt(val, 0, strconv.IntSize) - *i.v = int(v) - if err != nil { - return fmt.Errorf("bad value for int flag") - } - return i.Check(val) -} - -func (i *intValue) Get() interface{} { return *i.v } - -func (i *intValue) String() string { - if i == nil || i.v == nil { - return strconv.Itoa(0) - } - return strconv.Itoa(*i.v) -} - -func (s *intValue) Predict(prefix string) []string { - if s.Predictor != nil { - return s.Predictor.Predict(prefix) - } - return []string{""} -} - -type durationValue struct { - v *time.Duration - predict.Config -} - -func newDurationValue(val time.Duration, p *time.Duration, c predict.Config) *durationValue { - *p = val - return &durationValue{v: p, Config: c} -} - -func (i *durationValue) Set(val string) error { - v, err := time.ParseDuration(val) - *i.v = v - if err != nil { - return fmt.Errorf("bad value for duration flag") - } - return i.Check(val) -} - -func (i *durationValue) Get() interface{} { return *i.v } - -func (i *durationValue) String() string { - if i == nil || i.v == nil { - return time.Duration(0).String() - } - return i.v.String() -} - -func (s *durationValue) Predict(prefix string) []string { - if s.Predictor != nil { - return s.Predictor.Predict(prefix) - } - return []string{""} -} +func formatDuration(v time.Duration) string { return v.String() } diff --git a/compflag/flags.go b/compflag/flags.go new file mode 100644 index 0000000..91b413a --- /dev/null +++ b/compflag/flags.go @@ -0,0 +1,259 @@ +package compflag + +import ( + "flag" + "fmt" + "time" + + "github.com/posener/complete/v2/predict" +) + +// Code auto generated with `go run ./gen`. DO NOT EDIT + +//go:generate go run ./gen + +// String if a flag function for a flag of type string. +func String(name string, value string, usage string, options ...predict.Option) *string { + return CommandLine.String(name, value, usage, options...) +} + +// StringVar if a flag function for a flag of already exiting variable of type string. +func StringVar(v *string, name string, value string, usage string, options ...predict.Option) { + CommandLine.StringVar(v, name, value, usage, options...) +} + +// Bool if a flag function for a flag of type bool. +func Bool(name string, value bool, usage string, options ...predict.Option) *bool { + return CommandLine.Bool(name, value, usage, options...) +} + +// BoolVar if a flag function for a flag of already exiting variable of type bool. +func BoolVar(v *bool, name string, value bool, usage string, options ...predict.Option) { + CommandLine.BoolVar(v, name, value, usage, options...) +} + +// Int if a flag function for a flag of type int. +func Int(name string, value int, usage string, options ...predict.Option) *int { + return CommandLine.Int(name, value, usage, options...) +} + +// IntVar if a flag function for a flag of already exiting variable of type int. +func IntVar(v *int, name string, value int, usage string, options ...predict.Option) { + CommandLine.IntVar(v, name, value, usage, options...) +} + +// Duration if a flag function for a flag of type time.Duration. +func Duration(name string, value time.Duration, usage string, options ...predict.Option) *time.Duration { + return CommandLine.Duration(name, value, usage, options...) +} + +// DurationVar if a flag function for a flag of already exiting variable of type time.Duration. +func DurationVar(v *time.Duration, name string, value time.Duration, usage string, options ...predict.Option) { + CommandLine.DurationVar(v, name, value, usage, options...) +} + +// String if a flag function for a flag of type string. +func (fs *FlagSet) String(name string, value string, usage string, options ...predict.Option) *string { + p := new(string) + fs.StringVar(p, name, value, usage, options...) + return p +} + +// StringVar if a flag function for a flag of already exiting variable of type string. +func (fs *FlagSet) StringVar(p *string, name string, value string, usage string, options ...predict.Option) { + (*flag.FlagSet)(fs).Var(newStringValue(value, p, predict.Options(options...)), name, usage) +} + +// Bool if a flag function for a flag of type bool. +func (fs *FlagSet) Bool(name string, value bool, usage string, options ...predict.Option) *bool { + p := new(bool) + fs.BoolVar(p, name, value, usage, options...) + return p +} + +// BoolVar if a flag function for a flag of already exiting variable of type bool. +func (fs *FlagSet) BoolVar(p *bool, name string, value bool, usage string, options ...predict.Option) { + (*flag.FlagSet)(fs).Var(newBoolValue(value, p, predict.Options(options...)), name, usage) +} + +// Int if a flag function for a flag of type int. +func (fs *FlagSet) Int(name string, value int, usage string, options ...predict.Option) *int { + p := new(int) + fs.IntVar(p, name, value, usage, options...) + return p +} + +// IntVar if a flag function for a flag of already exiting variable of type int. +func (fs *FlagSet) IntVar(p *int, name string, value int, usage string, options ...predict.Option) { + (*flag.FlagSet)(fs).Var(newIntValue(value, p, predict.Options(options...)), name, usage) +} + +// Duration if a flag function for a flag of type time.Duration. +func (fs *FlagSet) Duration(name string, value time.Duration, usage string, options ...predict.Option) *time.Duration { + p := new(time.Duration) + fs.DurationVar(p, name, value, usage, options...) + return p +} + +// DurationVar if a flag function for a flag of already exiting variable of type time.Duration. +func (fs *FlagSet) DurationVar(p *time.Duration, name string, value time.Duration, usage string, options ...predict.Option) { + (*flag.FlagSet)(fs).Var(newDurationValue(value, p, predict.Options(options...)), name, usage) +} + +// ============================================================================================== // + +type stringValue struct { + v *string + predict.Config +} + +func newStringValue(val string, p *string, c predict.Config) *stringValue { + *p = val + return &stringValue{v: p, Config: c} +} + +func (v *stringValue) Set(val string) error { + var err error + *v.v, err = parseString(val) + if err != nil { + return fmt.Errorf("bad value for String flag") + } + return v.Check(val) +} + +func (v *stringValue) Get() interface{} { + return *v.v +} + +func (v *stringValue) String() string { + if v == nil || v.v == nil { + return "" + } + return formatString(*v.v) +} + +func (v *stringValue) Predict(prefix string) []string { + if v.Predictor != nil { + return v.Predictor.Predict(prefix) + } + return []string{""} +} + +// ============================================================================================== // + +type boolValue struct { + v *bool + predict.Config +} + +func newBoolValue(val bool, p *bool, c predict.Config) *boolValue { + *p = val + return &boolValue{v: p, Config: c} +} + +func (v *boolValue) Set(val string) error { + var err error + *v.v, err = parseBool(val) + if err != nil { + return fmt.Errorf("bad value for Bool flag") + } + return v.Check(val) +} + +func (v *boolValue) Get() interface{} { + return *v.v +} + +func (v *boolValue) String() string { + if v == nil || v.v == nil { + return "" + } + return formatBool(*v.v) +} + +func (v *boolValue) IsBoolFlag() bool { return true } + +func (v *boolValue) Predict(prefix string) []string { + if v.Predictor != nil { + return v.Predictor.Predict(prefix) + } + return predictBool(*v.v, prefix) +} + +// ============================================================================================== // + +type intValue struct { + v *int + predict.Config +} + +func newIntValue(val int, p *int, c predict.Config) *intValue { + *p = val + return &intValue{v: p, Config: c} +} + +func (v *intValue) Set(val string) error { + var err error + *v.v, err = parseInt(val) + if err != nil { + return fmt.Errorf("bad value for Int flag") + } + return v.Check(val) +} + +func (v *intValue) Get() interface{} { + return *v.v +} + +func (v *intValue) String() string { + if v == nil || v.v == nil { + return "" + } + return formatInt(*v.v) +} + +func (v *intValue) Predict(prefix string) []string { + if v.Predictor != nil { + return v.Predictor.Predict(prefix) + } + return []string{""} +} + +// ============================================================================================== // + +type durationValue struct { + v *time.Duration + predict.Config +} + +func newDurationValue(val time.Duration, p *time.Duration, c predict.Config) *durationValue { + *p = val + return &durationValue{v: p, Config: c} +} + +func (v *durationValue) Set(val string) error { + var err error + *v.v, err = parseDuration(val) + if err != nil { + return fmt.Errorf("bad value for Duration flag") + } + return v.Check(val) +} + +func (v *durationValue) Get() interface{} { + return *v.v +} + +func (v *durationValue) String() string { + if v == nil || v.v == nil { + return "" + } + return formatDuration(*v.v) +} + +func (v *durationValue) Predict(prefix string) []string { + if v.Predictor != nil { + return v.Predictor.Predict(prefix) + } + return []string{""} +} diff --git a/compflag/gen/flags.go.gotmpl b/compflag/gen/flags.go.gotmpl new file mode 100644 index 0000000..9b65dd5 --- /dev/null +++ b/compflag/gen/flags.go.gotmpl @@ -0,0 +1,83 @@ +package compflag + +// Code auto generated with `go run ./gen`. DO NOT EDIT + +//go:generate go run ./gen + + +{{ range . }} + +// {{ .Name }} if a flag function for a flag of type {{ .Type }}. +func {{ .Name }}(name string, value {{ .Type }}, usage string, options ...predict.Option) *{{ .Type }} { + return CommandLine.{{ .Name }}(name, value, usage, options...) +} + +// {{ .Name }}Var if a flag function for a flag of already exiting variable of type {{ .Type }}. +func {{ .Name }}Var(v *{{ .Type }}, name string, value {{ .Type }}, usage string, options ...predict.Option) { + CommandLine.{{ .Name }}Var(v, name, value, usage, options...) +} + +{{ end }} + +{{ range . }} + +// {{ .Name }} if a flag function for a flag of type {{ .Type }}. +func (fs *FlagSet) {{ .Name }}(name string, value {{ .Type }}, usage string, options ...predict.Option) *{{ .Type }} { + p := new({{ .Type }}) + fs.{{.Name}}Var(p, name, value, usage, options...) + return p +} + +// {{ .Name }}Var if a flag function for a flag of already exiting variable of type {{ .Type }}. +func (fs *FlagSet) {{ .Name }}Var(p *{{ .Type }}, name string, value {{ .Type }}, usage string, options ...predict.Option) { + (*flag.FlagSet)(fs).Var({{ .NewInternalTypeFuncName }}(value, p, predict.Options(options...)), name, usage) +} + +{{ end }} + +{{ range . }} + +// ============================================================================================== // + +type {{ .InternalTypeName }} struct { + v *{{ .Type }} + predict.Config +} + +func {{ .NewInternalTypeFuncName }}(val {{ .Type }}, p *{{ .Type }}, c predict.Config) *{{ .InternalTypeName }} { + *p = val + return &{{ .InternalTypeName}} {v: p, Config: c} +} + +func (v *{{ .InternalTypeName }}) Set(val string) error { + var err error + *v.v, err = parse{{ .Name }}(val) + if err != nil { + return fmt.Errorf("bad value for {{ .Name }} flag") + } + return v.Check(val) +} + +func (v *{{ .InternalTypeName }}) Get() interface{} { + return *v.v +} + +func (v *{{ .InternalTypeName }}) String() string { + if v == nil || v.v == nil { + return "" + } + return format{{ .Name }}(*v.v) +} + +{{ if .IsBool }} +func (v *{{ .InternalTypeName }}) IsBoolFlag() bool { return true } +{{ end}} + +func (v *{{ .InternalTypeName }}) Predict(prefix string) []string { + if v.Predictor != nil { + return v.Predictor.Predict(prefix) + } + return {{ if .CustomPredict }}predict{{ .Name }}(*v.v, prefix){{ else }}[]string{""}{{ end }} +} + +{{ end }} \ No newline at end of file diff --git a/compflag/gen/main.go b/compflag/gen/main.go new file mode 100644 index 0000000..cbea96f --- /dev/null +++ b/compflag/gen/main.go @@ -0,0 +1,67 @@ +// Generates flags.go. +package main + +import ( + "log" + "os" + "path/filepath" + "strings" + "text/template" + + "github.com/posener/script" +) + +const tmplGlob = "gen/*.go.gotmpl" + +type flag struct { + Name string + Type string + IsBool bool + CustomPredict bool +} + +func (f flag) NewInternalTypeFuncName() string { + return "new" + strings.Title(f.InternalTypeName()) +} + +func (f flag) InternalTypeName() string { + return strings.ToLower(f.Name[:1]) + f.Name[1:] + "Value" +} + +var flags = []flag{ + {Name: "String", Type: "string"}, + {Name: "Bool", Type: "bool", IsBool: true, CustomPredict: true}, + {Name: "Int", Type: "int"}, + {Name: "Duration", Type: "time.Duration"}, +} + +var tmpl = template.Must(template.ParseGlob(tmplGlob)) + +func main() { + for _, t := range tmpl.Templates() { + fileName := outFileName(t.Name()) + f, err := os.Create(fileName) + if err != nil { + panic(err) + } + defer f.Close() + + log.Printf("Writing %s", fileName) + err = t.Execute(f, flags) + if err != nil { + panic(err) + } + + // Format the file. + err = script.ExecHandleStderr(os.Stderr, "goimports", "-w", fileName).ToStdout() + if err != nil { + panic(err) + } + } +} + +func outFileName(templateName string) string { + name := filepath.Base(templateName) + // Remove .gotmpl suffix. + return name[:strings.LastIndex(name, ".")] +}