Skip to content

Commit 165d802

Browse files
Add a Sorted iterator on flag set for help text and keep All unordered (#210)
1 parent 1980fb2 commit 165d802

3 files changed

Lines changed: 81 additions & 7 deletions

File tree

command.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -653,7 +653,7 @@ func writeSubcommands(cmd *Command, s *strings.Builder) error {
653653
func writeFlags(cmd *Command, s *strings.Builder) error {
654654
tw := style.Tabwriter(s)
655655

656-
for name, fl := range cmd.flags.All() {
656+
for name, fl := range cmd.flags.Sorted() {
657657
var shorthand string
658658
if fl.Short() != publicflag.NoShortHand {
659659
shorthand = "-" + string(fl.Short())

internal/flag/set.go

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"errors"
55
"fmt"
66
"iter"
7+
"maps"
78
"os"
89
"slices"
910
"strings"
@@ -200,9 +201,9 @@ func (s *Set) Parse(args []string) error {
200201
return nil
201202
}
202203

203-
// All returns an iterator through the flags in the flagset
204+
// Sorted returns an iterator through the flags in the flagset
204205
// in alphabetical order by name.
205-
func (s *Set) All() iter.Seq2[string, Value] {
206+
func (s *Set) Sorted() iter.Seq2[string, Value] {
206207
return func(yield func(string, Value) bool) {
207208
names := make([]string, 0, len(s.flags))
208209
for name := range s.flags {
@@ -219,6 +220,12 @@ func (s *Set) All() iter.Seq2[string, Value] {
219220
}
220221
}
221222

223+
// All returns an iterator through the flags in the flagset
224+
// in alphabetical order by name.
225+
func (s *Set) All() iter.Seq2[string, Value] {
226+
return maps.All(s.flags)
227+
}
228+
222229
// applyEnvVars looks up each configured environment variable and applies its value
223230
// to the corresponding flag. It is called at the start of Parse so that CLI args
224231
// parsed afterward naturally override these values.

internal/flag/set_test.go

Lines changed: 71 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2307,7 +2307,7 @@ func TestHelpVersion(t *testing.T) {
23072307
}
23082308
}
23092309

2310-
func TestAll(t *testing.T) {
2310+
func TestSorted(t *testing.T) {
23112311
tests := []struct {
23122312
newSet func(t *testing.T) *flag.Set
23132313
test func(t *testing.T, set *flag.Set)
@@ -2320,7 +2320,7 @@ func TestAll(t *testing.T) {
23202320
},
23212321
test: func(t *testing.T, set *flag.Set) {
23222322
// Iterator should yield no values
2323-
got := maps.Collect(set.All())
2323+
got := maps.Collect(set.Sorted())
23242324
test.Equal(t, len(got), 0)
23252325
},
23262326
},
@@ -2353,8 +2353,7 @@ func TestAll(t *testing.T) {
23532353
return set
23542354
},
23552355
test: func(t *testing.T, set *flag.Set) {
2356-
// Iterator should yield no values
2357-
next, stop := iter.Pull2(set.All())
2356+
next, stop := iter.Pull2(set.Sorted())
23582357
defer stop()
23592358

23602359
// Should now be in alphabetical order
@@ -2400,6 +2399,74 @@ func TestAll(t *testing.T) {
24002399
}
24012400
}
24022401

2402+
func TestAll(t *testing.T) {
2403+
tests := []struct {
2404+
newSet func(t *testing.T) *flag.Set
2405+
test func(t *testing.T, set *flag.Set)
2406+
name string
2407+
}{
2408+
{
2409+
name: "empty",
2410+
newSet: func(t *testing.T) *flag.Set {
2411+
return flag.NewSet()
2412+
},
2413+
test: func(t *testing.T, set *flag.Set) {
2414+
// Iterator should yield no values
2415+
got := maps.Collect(set.All())
2416+
test.Equal(t, len(got), 0)
2417+
},
2418+
},
2419+
{
2420+
name: "full",
2421+
newSet: func(t *testing.T) *flag.Set {
2422+
set := flag.NewSet()
2423+
2424+
verbose, err := flag.New(new(bool), "verbose", 'v', "Show verbose info", flag.Config[bool]{})
2425+
test.Ok(t, err)
2426+
2427+
debug, err := flag.New(new(bool), "debug", 'd', "Show debug info", flag.Config[bool]{})
2428+
test.Ok(t, err)
2429+
2430+
thing, err := flag.New(new(string), "thing", 't', "A thing", flag.Config[string]{})
2431+
test.Ok(t, err)
2432+
2433+
number, err := flag.New(new(int), "number", 'n', "Number of times", flag.Config[int]{})
2434+
test.Ok(t, err)
2435+
2436+
duration, err := flag.New(new(time.Duration), "duration", 'D', "The time to do something for", flag.Config[time.Duration]{})
2437+
test.Ok(t, err)
2438+
2439+
test.Ok(t, flag.AddToSet(set, verbose))
2440+
test.Ok(t, flag.AddToSet(set, debug))
2441+
test.Ok(t, flag.AddToSet(set, thing))
2442+
test.Ok(t, flag.AddToSet(set, number))
2443+
test.Ok(t, flag.AddToSet(set, duration))
2444+
2445+
return set
2446+
},
2447+
test: func(t *testing.T, set *flag.Set) {
2448+
// Should get everything back, but order is not deterministic
2449+
all := maps.Collect(set.All())
2450+
2451+
want := []string{"verbose", "debug", "thing", "number", "duration"}
2452+
slices.Sort(want)
2453+
2454+
got := slices.Collect(maps.Keys(all))
2455+
slices.Sort(got)
2456+
2457+
test.EqualFunc(t, got, want, slices.Equal)
2458+
},
2459+
},
2460+
}
2461+
2462+
for _, tt := range tests {
2463+
t.Run(tt.name, func(t *testing.T) {
2464+
set := tt.newSet(t)
2465+
tt.test(t, set)
2466+
})
2467+
}
2468+
}
2469+
24032470
func BenchmarkParse(b *testing.B) {
24042471
set := flag.NewSet()
24052472
f, err := flag.New(new(int), "count", 'c', "Count something", flag.Config[int]{})

0 commit comments

Comments
 (0)