Skip to content

Commit 6b9aa15

Browse files
Provide a way of iterating over all flags
1 parent 76e9cea commit 6b9aa15

2 files changed

Lines changed: 116 additions & 0 deletions

File tree

internal/flag/set.go

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"bytes"
55
"errors"
66
"fmt"
7+
"iter"
78
"slices"
89
"strings"
910

@@ -187,6 +188,25 @@ func (s *Set) Parse(args []string) (err error) {
187188
return nil
188189
}
189190

191+
// All returns an iterator through the flags in the flagset
192+
// in alphabetical order by name.
193+
func (s *Set) All() iter.Seq2[string, Value] {
194+
return func(yield func(string, Value) bool) {
195+
names := make([]string, 0, len(s.flags))
196+
for name := range s.flags {
197+
names = append(names, name)
198+
}
199+
200+
slices.Sort(names)
201+
202+
for _, name := range names {
203+
if !yield(name, s.flags[name]) {
204+
return
205+
}
206+
}
207+
}
208+
}
209+
190210
// Usage returns a string containing the usage info of all flags in the set.
191211
func (s *Set) Usage() (string, error) {
192212
buf := &bytes.Buffer{}

internal/flag/set_test.go

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,11 @@ package flag_test
33
import (
44
goflag "flag"
55
"fmt"
6+
"iter"
7+
"maps"
68
"slices"
79
"testing"
10+
"time"
811

912
publicflag "go.followtheprocess.codes/cli/flag"
1013
"go.followtheprocess.codes/cli/internal/flag"
@@ -1209,6 +1212,99 @@ func TestHelpVersion(t *testing.T) {
12091212
}
12101213
}
12111214

1215+
func TestAll(t *testing.T) {
1216+
tests := []struct {
1217+
newSet func(t *testing.T) *flag.Set
1218+
test func(t *testing.T, set *flag.Set)
1219+
name string
1220+
}{
1221+
{
1222+
name: "empty",
1223+
newSet: func(t *testing.T) *flag.Set {
1224+
return flag.NewSet()
1225+
},
1226+
test: func(t *testing.T, set *flag.Set) {
1227+
// Iterator should yield no values
1228+
got := maps.Collect(set.All())
1229+
test.Equal(t, len(got), 0)
1230+
},
1231+
},
1232+
{
1233+
name: "full",
1234+
newSet: func(t *testing.T) *flag.Set {
1235+
set := flag.NewSet()
1236+
1237+
verbose, err := flag.New(new(bool), "verbose", 'v', "Show verbose info", flag.Config[bool]{})
1238+
test.Ok(t, err)
1239+
1240+
debug, err := flag.New(new(bool), "debug", 'd', "Show debug info", flag.Config[bool]{})
1241+
test.Ok(t, err)
1242+
1243+
thing, err := flag.New(new(string), "thing", 't', "A thing", flag.Config[string]{})
1244+
test.Ok(t, err)
1245+
1246+
number, err := flag.New(new(int), "number", 'n', "Number of times", flag.Config[int]{})
1247+
test.Ok(t, err)
1248+
1249+
duration, err := flag.New(new(time.Duration), "duration", 'D', "The time to do something for", flag.Config[time.Duration]{})
1250+
test.Ok(t, err)
1251+
1252+
test.Ok(t, flag.AddToSet(set, verbose))
1253+
test.Ok(t, flag.AddToSet(set, debug))
1254+
test.Ok(t, flag.AddToSet(set, thing))
1255+
test.Ok(t, flag.AddToSet(set, number))
1256+
test.Ok(t, flag.AddToSet(set, duration))
1257+
1258+
return set
1259+
},
1260+
test: func(t *testing.T, set *flag.Set) {
1261+
// Iterator should yield no values
1262+
next, stop := iter.Pull2(set.All())
1263+
defer stop()
1264+
1265+
// Should now be in alphabetical order
1266+
name, fl, ok := next()
1267+
test.True(t, ok)
1268+
test.Equal(t, name, "debug")
1269+
test.Equal(t, fl.Name(), "debug")
1270+
1271+
name, fl, ok = next()
1272+
test.True(t, ok)
1273+
test.Equal(t, name, "duration")
1274+
test.Equal(t, fl.Name(), "duration")
1275+
1276+
name, fl, ok = next()
1277+
test.True(t, ok)
1278+
test.Equal(t, name, "number")
1279+
test.Equal(t, fl.Name(), "number")
1280+
1281+
name, fl, ok = next()
1282+
test.True(t, ok)
1283+
test.Equal(t, name, "thing")
1284+
test.Equal(t, fl.Name(), "thing")
1285+
1286+
name, fl, ok = next()
1287+
test.True(t, ok)
1288+
test.Equal(t, name, "verbose")
1289+
test.Equal(t, fl.Name(), "verbose")
1290+
1291+
// Thats it
1292+
name, fl, ok = next()
1293+
test.False(t, ok)
1294+
test.Equal(t, name, "")
1295+
test.Equal(t, fl, nil)
1296+
},
1297+
},
1298+
}
1299+
1300+
for _, tt := range tests {
1301+
t.Run(tt.name, func(t *testing.T) {
1302+
set := tt.newSet(t)
1303+
tt.test(t, set)
1304+
})
1305+
}
1306+
}
1307+
12121308
func TestUsage(t *testing.T) {
12131309
tests := []struct {
12141310
newSet func(t *testing.T) *flag.Set // Function to build the flag set under test

0 commit comments

Comments
 (0)