Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

flagcodec: enable flag normalization #255

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
78 changes: 60 additions & 18 deletions pkg/flagcodec/flagcodec.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,10 @@ type Val struct {
}

type Flags struct {
command string
args map[string]Val
keys []string
command string
args map[string]Val
keys []string
processFlagName func(string) string
}

// ParseArgvKeyValue parses a clean (trimmed) argv whose components
Expand All @@ -51,9 +52,24 @@ type Flags struct {
// "--opt=foo"
// AND NOT
// "--opt", "foo"
// The value of argv[0], whatever it is, is taken at command.
func ParseArgvKeyValue(args []string) *Flags {
return ParseArgvKeyValueWithCommand("", args)
func ParseArgvKeyValue(args []string, opts ...Option) *Flags {
ret := &Flags{
command: "",
args: make(map[string]Val),
processFlagName: func(v string) string { return v },
}
for _, opt := range opts {
opt(ret)
}
for _, arg := range args {
fields := strings.SplitN(arg, "=", 2)
if len(fields) == 1 {
ret.SetToggle(fields[0])
continue
}
ret.SetOption(fields[0], fields[1])
}
return ret
}

// ParseArgvKeyValueWithCommand parses a clean (trimmed) argv whose components
Expand All @@ -64,21 +80,43 @@ func ParseArgvKeyValue(args []string) *Flags {
// "--opt=foo"
// AND NOT
// "--opt", "foo"
// The command is supplied explicitely as parameter.
// The command is supplied explicitly as parameter.
// DEPRECATED: use ParseArgvValue and WithCommand option
func ParseArgvKeyValueWithCommand(command string, args []string) *Flags {
ret := &Flags{
command: command,
args: make(map[string]Val),
return ParseArgvKeyValue(args, WithCommand(command))
}

type Option func(*Flags) *Flags

func normalizeFlagName(v string) string {
if len(v) == 3 && v[0] == '-' && v[1] == '-' {
// single char, double dash flag (ugly?), fix it
return v[1:]
}
for _, arg := range args {
fields := strings.SplitN(arg, "=", 2)
if len(fields) == 1 {
ret.SetToggle(fields[0])
continue
}
ret.SetOption(fields[0], fields[1])
// everything else pass through silently
return v
}

// WithFlagNormalization optionally enables flag normalization.
// The canonical representation of flags in this package is:
// * single-dash for one-char flags (-v, -h)
// * double-dash for multi-char flags (--foo, --long-option)
// pflag allows one-char to have one or two dashes. For flagcodec
// these were different options. When normalization is enabled,
// though, all flag names are processed to adhere to the canonical
// representation, so flagcodec will treat `--v` and `-v` to
// be the same flag. Since this is possibly breaking change,
// this treatment is opt-in.
func WithFlagNormalization(fl *Flags) *Flags {
fl.processFlagName = normalizeFlagName
return fl
}

func WithCommand(command string) Option {
return func(fl *Flags) *Flags {
fl.command = command
return fl
}
return ret
}

func (fl *Flags) recordFlag(name string) {
Expand All @@ -99,13 +137,15 @@ func (fl *Flags) forgetFlag(name string) {
}

func (fl *Flags) SetToggle(name string) {
name = fl.processFlagName(name)
fl.recordFlag(name)
fl.args[name] = Val{
Kind: FlagToggle,
}
}

func (fl *Flags) SetOption(name, data string) {
name = fl.processFlagName(name)
fl.recordFlag(name)
fl.args[name] = Val{
Kind: FlagOption,
Expand All @@ -114,6 +154,7 @@ func (fl *Flags) SetOption(name, data string) {
}

func (fl *Flags) Delete(name string) {
name = fl.processFlagName(name)
fl.forgetFlag(name)
delete(fl.args, name)
}
Expand All @@ -139,6 +180,7 @@ func (fl *Flags) Argv() []string {
}

func (fl *Flags) GetFlag(name string) (Val, bool) {
name = fl.processFlagName(name)
if val, ok := fl.args[name]; ok {
return val, ok
}
Expand Down
Loading