Skip to content

Commit

Permalink
Merge pull request #1471 from dearchap/collapse_flag_interface
Browse files Browse the repository at this point in the history
Collapse flag interface
  • Loading branch information
dearchap authored Sep 5, 2022
2 parents 3216104 + 7e80d20 commit 0a88df4
Show file tree
Hide file tree
Showing 31 changed files with 683 additions and 712 deletions.
4 changes: 1 addition & 3 deletions app.go
Original file line number Diff line number Diff line change
Expand Up @@ -227,9 +227,7 @@ func (a *App) Setup() {

a.flagCategories = newFlagCategories()
for _, fl := range a.Flags {
if cf, ok := fl.(CategorizableFlag); ok {
a.flagCategories.AddFlag(cf.GetCategory(), cf)
}
a.flagCategories.AddFlag(fl.GetCategory(), fl)
}

if a.Metadata == nil {
Expand Down
20 changes: 20 additions & 0 deletions app_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2252,6 +2252,26 @@ func (c *customBoolFlag) IsSet() bool {
return false
}

func (c *customBoolFlag) IsRequired() bool {
return false
}

func (c *customBoolFlag) IsVisible() bool {
return false
}

func (c *customBoolFlag) GetCategory() string {
return ""
}

func (c *customBoolFlag) GetEnvVars() []string {
return nil
}

func (c *customBoolFlag) GetDefaultText() string {
return ""
}

func TestCustomFlagsUnused(t *testing.T) {
app := &App{
Flags: []Flag{&customBoolFlag{"custom"}},
Expand Down
18 changes: 7 additions & 11 deletions category.go
Original file line number Diff line number Diff line change
Expand Up @@ -101,9 +101,7 @@ func newFlagCategories() FlagCategories {
func newFlagCategoriesFromFlags(fs []Flag) FlagCategories {
fc := newFlagCategories()
for _, fl := range fs {
if cf, ok := fl.(CategorizableFlag); ok {
fc.AddFlag(cf.GetCategory(), cf)
}
fc.AddFlag(fl.GetCategory(), fl)
}

return fc
Expand Down Expand Up @@ -138,7 +136,7 @@ type VisibleFlagCategory interface {
// Name returns the category name string
Name() string
// Flags returns a slice of VisibleFlag sorted by name
Flags() []VisibleFlag
Flags() []Flag
}

type defaultVisibleFlagCategory struct {
Expand All @@ -150,21 +148,19 @@ func (fc *defaultVisibleFlagCategory) Name() string {
return fc.name
}

func (fc *defaultVisibleFlagCategory) Flags() []VisibleFlag {
func (fc *defaultVisibleFlagCategory) Flags() []Flag {
vfNames := []string{}
for flName, fl := range fc.m {
if vf, ok := fl.(VisibleFlag); ok {
if vf.IsVisible() {
vfNames = append(vfNames, flName)
}
if fl.IsVisible() {
vfNames = append(vfNames, flName)
}
}

sort.Strings(vfNames)

ret := make([]VisibleFlag, len(vfNames))
ret := make([]Flag, len(vfNames))
for i, flName := range vfNames {
ret[i] = fc.m[flName].(VisibleFlag)
ret[i] = fc.m[flName]
}

return ret
Expand Down
2 changes: 1 addition & 1 deletion context.go
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ func (cCtx *Context) lookupFlagSet(name string) *flag.FlagSet {
func (cCtx *Context) checkRequiredFlags(flags []Flag) requiredFlagsErr {
var missingFlags []string
for _, f := range flags {
if rf, ok := f.(RequiredFlag); ok && rf.IsRequired() {
if f.IsRequired() {
var flagPresent bool
var flagName string

Expand Down
8 changes: 2 additions & 6 deletions docs.go
Original file line number Diff line number Diff line change
Expand Up @@ -116,11 +116,7 @@ func prepareFlags(
addDetails bool,
) []string {
args := []string{}
for _, f := range flags {
flag, ok := f.(DocGenerationFlag)
if !ok {
continue
}
for _, flag := range flags {
modifiedArg := opener

for _, s := range flag.Names() {
Expand Down Expand Up @@ -151,7 +147,7 @@ func prepareFlags(
}

// flagDetails returns a string containing the flags metadata
func flagDetails(flag DocGenerationFlag) string {
func flagDetails(flag Flag) string {
description := flag.GetUsage()
value := flag.GetValue()
if value != "" {
Expand Down
9 changes: 2 additions & 7 deletions fish.go
Original file line number Diff line number Diff line change
Expand Up @@ -114,20 +114,15 @@ func (a *App) prepareFishCommands(commands []*Command, allCommands *[]string, pr

func (a *App) prepareFishFlags(flags []Flag, previousCommands []string) []string {
completions := []string{}
for _, f := range flags {
flag, ok := f.(DocGenerationFlag)
if !ok {
continue
}

for _, flag := range flags {
completion := &strings.Builder{}
completion.WriteString(fmt.Sprintf(
"complete -c %s -n '%s'",
a.Name,
a.fishSubcommandHelper(previousCommands),
))

fishAddFileFlag(f, completion)
fishAddFileFlag(flag, completion)

for idx, opt := range flag.Names() {
if idx == 0 {
Expand Down
5 changes: 4 additions & 1 deletion flag-spec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
# `genflags.Spec` type that maps to this file structure.

flag_types:
bool: {}
bool:
no_default_text: true
float64: {}
int64: {}
int: {}
Expand All @@ -12,12 +13,14 @@ flag_types:
uint: {}

string:
no_default_text: true
struct_fields:
- { name: TakesFile, type: bool }
Generic:
struct_fields:
- { name: TakesFile, type: bool }
Path:
no_default_text: true
struct_fields:
- { name: TakesFile, type: bool }

Expand Down
67 changes: 23 additions & 44 deletions flag.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,55 +89,40 @@ func (f FlagsByName) Swap(i, j int) {
// this interface be implemented.
type Flag interface {
fmt.Stringer

// Apply Flag settings to the given flag set
Apply(*flag.FlagSet) error

// All possible names for this flag
Names() []string
IsSet() bool
}

// RequiredFlag is an interface that allows us to mark flags as required
// it allows flags required flags to be backwards compatible with the Flag interface
type RequiredFlag interface {
Flag
// Whether the flag has been set or not
IsSet() bool

// whether the flag is a required flag or not
IsRequired() bool
}

// DocGenerationFlag is an interface that allows documentation generation for the flag
type DocGenerationFlag interface {
Flag
// IsVisible returns true if the flag is not hidden, otherwise false
IsVisible() bool

// TakesValue returns true if the flag takes a value, otherwise false
TakesValue() bool
// Returns the category of the flag
GetCategory() string

// GetUsage returns the usage string for the flag
GetUsage() string

// GetValue returns the flags value as string representation and an empty
// string if the flag takes no value at all.
GetValue() string

// GetDefaultText returns the default text for this flag
GetDefaultText() string

// GetEnvVars returns the env vars for this flag
GetEnvVars() []string
}

// VisibleFlag is an interface that allows to check if a flag is visible
type VisibleFlag interface {
Flag

// IsVisible returns true if the flag is not hidden, otherwise false
IsVisible() bool
}
// TakesValue returns true if the flag takes a value, otherwise false
TakesValue() bool

// CategorizableFlag is an interface that allows us to potentially
// use a flag in a categorized representation.
type CategorizableFlag interface {
VisibleFlag
// GetDefaultText returns the default text for this flag
GetDefaultText() string

GetCategory() string
// GetValue returns the flags value as string representation and an empty
// string if the flag takes no value at all.
GetValue() string
}

func flagSet(name string, flags []Flag) (*flag.FlagSet, error) {
Expand Down Expand Up @@ -197,7 +182,7 @@ func normalizeFlags(flags []Flag, set *flag.FlagSet) error {
func visibleFlags(fl []Flag) []Flag {
var visible []Flag
for _, f := range fl {
if vf, ok := f.(VisibleFlag); ok && vf.IsVisible() {
if f.IsVisible() {
visible = append(visible, f)
}
}
Expand Down Expand Up @@ -293,29 +278,23 @@ func formatDefault(format string) string {
}

func stringifyFlag(f Flag) string {
// enforce DocGeneration interface on flags to avoid reflection
df, ok := f.(DocGenerationFlag)
if !ok {
return ""
}

placeholder, usage := unquoteUsage(df.GetUsage())
needsPlaceholder := df.TakesValue()
placeholder, usage := unquoteUsage(f.GetUsage())
needsPlaceholder := f.TakesValue()

if needsPlaceholder && placeholder == "" {
placeholder = defaultPlaceholder
}

defaultValueString := ""

if s := df.GetDefaultText(); s != "" {
if s := f.GetDefaultText(); s != "" {
defaultValueString = fmt.Sprintf(formatDefault("%s"), s)
}

usageWithDefault := strings.TrimSpace(usage + defaultValueString)

return withEnvHint(df.GetEnvVars(),
fmt.Sprintf("%s\t%s", prefixedNames(df.Names(), placeholder), usageWithDefault))
return withEnvHint(f.GetEnvVars(),
fmt.Sprintf("%s\t%s", prefixedNames(f.Names(), placeholder), usageWithDefault))
}

func stringifyIntSliceFlag(f *IntSliceFlag) string {
Expand Down
20 changes: 0 additions & 20 deletions flag_bool.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,21 +6,6 @@ import (
"strconv"
)

// TakesValue returns true of the flag takes a value, otherwise false
func (f *BoolFlag) TakesValue() bool {
return false
}

// GetUsage returns the usage string for the flag
func (f *BoolFlag) GetUsage() string {
return f.Usage
}

// GetCategory returns the category for the flag
func (f *BoolFlag) GetCategory() string {
return f.Category
}

// GetValue returns the flags value as string representation and an empty
// string if the flag takes no value at all.
func (f *BoolFlag) GetValue() string {
Expand All @@ -35,11 +20,6 @@ func (f *BoolFlag) GetDefaultText() string {
return fmt.Sprintf("%v", f.Value)
}

// GetEnvVars returns the env vars for this flag
func (f *BoolFlag) GetEnvVars() []string {
return f.EnvVars
}

// Apply populates the flag given the flag set and environment
func (f *BoolFlag) Apply(set *flag.FlagSet) error {
if val, source, found := flagFromEnvOrFile(f.EnvVars, f.FilePath); found {
Expand Down
28 changes: 0 additions & 28 deletions flag_duration.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,40 +6,12 @@ import (
"time"
)

// TakesValue returns true of the flag takes a value, otherwise false
func (f *DurationFlag) TakesValue() bool {
return true
}

// GetUsage returns the usage string for the flag
func (f *DurationFlag) GetUsage() string {
return f.Usage
}

// GetCategory returns the category for the flag
func (f *DurationFlag) GetCategory() string {
return f.Category
}

// GetValue returns the flags value as string representation and an empty
// string if the flag takes no value at all.
func (f *DurationFlag) GetValue() string {
return f.Value.String()
}

// GetDefaultText returns the default text for this flag
func (f *DurationFlag) GetDefaultText() string {
if f.DefaultText != "" {
return f.DefaultText
}
return f.GetValue()
}

// GetEnvVars returns the env vars for this flag
func (f *DurationFlag) GetEnvVars() []string {
return f.EnvVars
}

// Apply populates the flag given the flag set and environment
func (f *DurationFlag) Apply(set *flag.FlagSet) error {
if val, source, found := flagFromEnvOrFile(f.EnvVars, f.FilePath); found {
Expand Down
28 changes: 0 additions & 28 deletions flag_float64.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,40 +6,12 @@ import (
"strconv"
)

// TakesValue returns true of the flag takes a value, otherwise false
func (f *Float64Flag) TakesValue() bool {
return true
}

// GetUsage returns the usage string for the flag
func (f *Float64Flag) GetUsage() string {
return f.Usage
}

// GetCategory returns the category for the flag
func (f *Float64Flag) GetCategory() string {
return f.Category
}

// GetValue returns the flags value as string representation and an empty
// string if the flag takes no value at all.
func (f *Float64Flag) GetValue() string {
return fmt.Sprintf("%v", f.Value)
}

// GetDefaultText returns the default text for this flag
func (f *Float64Flag) GetDefaultText() string {
if f.DefaultText != "" {
return f.DefaultText
}
return f.GetValue()
}

// GetEnvVars returns the env vars for this flag
func (f *Float64Flag) GetEnvVars() []string {
return f.EnvVars
}

// Apply populates the flag given the flag set and environment
func (f *Float64Flag) Apply(set *flag.FlagSet) error {
if val, source, found := flagFromEnvOrFile(f.EnvVars, f.FilePath); found {
Expand Down
Loading

0 comments on commit 0a88df4

Please sign in to comment.