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

Collapse flag interface #1471

Merged
merged 4 commits into from
Sep 5, 2022
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
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