Skip to content

Commit 4474345

Browse files
authored
Cli api (#2)
* Add flagIterator * Add Cli wrapper * Add SetSwitch utility method - add newCliError - add builder pattern to Flagset - FlagSet.isFlag -> FlagSet.hasFlag - Fix setAsParsed bug on booleans - rename flag setter methods to Set<Type>(), fs.SetInt("n") - rename flag getter methods to <Type>(), fs.Int("n")
1 parent aa241f5 commit 4474345

File tree

5 files changed

+308
-123
lines changed

5 files changed

+308
-123
lines changed

demo/demo.go

Lines changed: 34 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -28,65 +28,58 @@ func (t *Todo) String() string {
2828
}
2929

3030
func main() {
31-
get := flago.NewFlagSet("get")
32-
get.Bool("all", false)
33-
get.Bool("reverse", false)
34-
get.Str("sort-by", "title")
35-
get.Str("by-title", "")
36-
get.Bool("primary", false)
37-
get.Bool("help", false)
31+
get := flago.NewFlagSet("get").
32+
SetSwitch("all"). // SetSwitch shorthand for SetBool(<flagname>, false)
33+
SetBool("reverse", false).
34+
SetStr("sort-by", "title").
35+
SetStr("by-title", "").
36+
SetBool("primary", false).
37+
SetSwitch("help") // Optional, cli adds a help flag if none was provided
3838

3939
args := os.Args
40-
if len(args) <= 2 {
41-
fmt.Println(GET_CMD_HELP)
42-
return
43-
}
4440

45-
action := args[1]
46-
args_to_parse := args[2:]
4741
todos := []Todo{
4842
{"foo", "describes foo", 3, false},
4943
{"bar", "describes bar", 8, false},
50-
{"yeet", "describes yeet", 2, false},
51-
{"boo", "describes boo", 7, true},
44+
{"yeet", "describes yeet", 12, false},
45+
{"boo", "describes boo", 9, true},
5246
}
5347

54-
if action != get.Name {
55-
fmt.Printf("Invalid action %s\n", action)
56-
fmt.Println(GET_CMD_HELP)
57-
return
58-
}
48+
cli := flago.NewCli()
49+
cli.Handle(get, func(fs *flago.FlagSet) error {
50+
HandleGet(fs, todos)
51+
return nil
52+
})
5953

60-
if err := get.ParseFlags(args_to_parse); err != nil {
54+
if err := cli.Execute(args); err != nil {
6155
fmt.Println(err)
62-
return
6356
}
57+
}
6458

65-
if get.IsParsed("all") {
66-
67-
HandleAll(get, todos)
68-
69-
} else if get.IsParsed("by-title") {
70-
71-
HandleByTitle(get, todos)
72-
73-
} else if get.IsParsed("primary") {
74-
75-
HandleByPrimary(get, todos)
76-
59+
func HandleGet(fs *flago.FlagSet, todos []Todo) {
60+
isAll := fs.IsParsed("all")
61+
isByTitle := fs.IsParsed("by-title")
62+
isPrimary := fs.IsParsed("primary")
63+
64+
if isAll {
65+
HandleAll(fs, todos)
66+
} else if isByTitle {
67+
HandleByTitle(fs, todos)
68+
} else if isPrimary {
69+
HandleByPrimary(fs, todos)
7770
} else {
7871
fmt.Println(GET_CMD_HELP)
7972
}
8073
}
8174

8275
func HandleAll(fs *flago.FlagSet, todos []Todo) {
83-
if fs.GetBool("help") {
76+
if fs.Bool("help") {
8477
fmt.Println(GET_ALL_HELP)
8578
return
8679
}
8780

88-
is_reverse := fs.GetBool("reverse")
89-
sort_by := fs.GetStr("sort-by")
81+
is_reverse := fs.Bool("reverse")
82+
sort_by := fs.Str("sort-by")
9083

9184
if sort_by == "title" {
9285
sort.Slice(todos, func(i, j int) bool {
@@ -110,20 +103,20 @@ func HandleAll(fs *flago.FlagSet, todos []Todo) {
110103
}
111104

112105
func HandleByTitle(fs *flago.FlagSet, todos []Todo) {
113-
if fs.GetBool("help") {
106+
if fs.Bool("help") {
114107
fmt.Println(GET_BY_TITLE_HELP)
115108
return
116109
}
117110

118-
title := fs.GetStr("by-title")
119-
title = strings.ToLower(title)
111+
title := strings.ToLower(fs.Str("by-title"))
120112
filtered := make([]Todo, 0)
121113

122114
for _, todo := range todos {
123115
if strings.ToLower(todo.title) == title {
124116
filtered = append(filtered, todo)
125117
}
126118
}
119+
127120
if len(filtered) > 0 {
128121
fmt.Println(filtered[0].String())
129122
} else {
@@ -132,7 +125,7 @@ func HandleByTitle(fs *flago.FlagSet, todos []Todo) {
132125
}
133126

134127
func HandleByPrimary(fs *flago.FlagSet, todos []Todo) {
135-
if fs.GetBool("help") {
128+
if fs.Bool("help") {
136129
fmt.Println(GET_PRIMARY_HELP)
137130
return
138131
}

errors.go

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,23 @@
11
package flago
22

33
import (
4-
"errors"
54
"fmt"
65
)
76

87
func newParseTypeError(value, datatype string) error {
9-
return errors.New(fmt.Sprintf("Error at parsing value \"%s\" to datatype %s\n", value, datatype))
8+
return fmt.Errorf("Error at parsing value \"%s\" to datatype %s\n", value, datatype)
109
}
1110

1211
func newUnknownDataTypeError(datatype, name string) error {
13-
return errors.New(fmt.Sprintf("Unexpected datatype \"%s\" for flag of name %s", datatype, name))
12+
return fmt.Errorf("Unexpected datatype \"%s\" for flag of name %s", datatype, name)
13+
}
14+
15+
func newMissingValueError(name string, i int) error {
16+
return fmt.Errorf("The key: %s is trying to access missing item at index: %d", name, i)
17+
}
18+
19+
func newCliError(cli *Cli) error {
20+
return fmt.Errorf("Available Subcommands: %v", cli.flagsetKeys())
1421
}
1522

1623
type invalidFlagAsValueError struct {

flago.go

Lines changed: 75 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1,76 +1,102 @@
11
package flago
22

3-
func (fs *FlagSet) ParseFlags(args_to_parse []string) error {
4-
args_copy := copySlice(args_to_parse)
3+
func (fs *FlagSet) ParseFlags(args []string) error {
4+
cp := copySlice(args)
55

66
// remove flag prefix from args
7-
for i, v := range args_copy {
8-
args_copy[i] = removeFlagPrefix(v)
7+
for i, v := range cp {
8+
cp[i] = removeFlagPrefix(v)
99
}
1010

11-
// get the tokens from args
12-
tokens := getTokens(args_copy)
11+
args = cp
1312

14-
// filter by flag names
15-
only_flags := make([]token, 0)
16-
for _, token := range tokens {
17-
if fs.isFlag(token.name) {
18-
only_flags = append(only_flags, token)
13+
iter := newFlagIterator(args)
14+
for !iter.is_empty() {
15+
key, ok := iter.next()
16+
if !fs.hasFlag(key) || !ok {
17+
continue
1918
}
20-
}
21-
22-
tokens = only_flags
2319

24-
// parse tokens into flag
25-
for _, token := range tokens {
26-
name := token.name
27-
value := token.value
28-
flag := fs.Flags[name]
20+
flag := fs.Flags[key]
21+
data_type := flag.Datatype
2922

30-
if flag.Datatype != "bool" && fs.isFlag(value) && value != "help" {
31-
return newInvalidFlagAsValueError(name, value)
32-
}
33-
34-
switch flag.Datatype {
35-
case "bool":
23+
if data_type == "bool" {
3624
flag.Value = true
37-
case "string":
38-
flag.Value = value
39-
case "int":
40-
v, err := parseInt(value)
41-
if err != nil {
42-
return newParseTypeError(value, "int")
25+
} else {
26+
value, ok := iter.next()
27+
if !ok {
28+
return newMissingValueError(key, iter.index)
4329
}
4430

45-
flag.Value = v
46-
47-
case "float":
48-
v, err := parseFloat(value)
49-
if err != nil {
50-
return newParseTypeError(value, "float")
31+
if fs.hasFlag(value) && value != "help" {
32+
return newInvalidFlagAsValueError(key, value)
5133
}
5234

53-
flag.Value = v
35+
switch data_type {
36+
case "string":
37+
flag.Value = value
5438

55-
default:
56-
return newUnknownDataTypeError(string(flag.Datatype), flag.Name)
39+
case "int":
40+
int_value, err := parseInt(value)
41+
if err != nil {
42+
return newParseTypeError(value, "int")
43+
}
44+
45+
flag.Value = int_value
46+
47+
case "float":
48+
float_value, err := parseFloat(value)
49+
if err != nil {
50+
return newParseTypeError(value, "float")
51+
}
52+
53+
flag.Value = float_value
54+
55+
default:
56+
return newUnknownDataTypeError(string(data_type), flag.Name)
57+
}
5758
}
58-
fs.setAsParsed(name)
59+
60+
fs.setAsParsed(key)
5961
}
6062

6163
return nil
6264
}
6365

64-
type token = struct {
65-
name string
66-
value string
66+
type flagIterator struct {
67+
args []string
68+
index int
69+
max int
70+
}
71+
72+
func newFlagIterator(args []string) flagIterator {
73+
index := 0
74+
max := len(args)
75+
return flagIterator{args, index, max}
6776
}
6877

69-
func getTokens(args []string) []token {
70-
tokens := make([]token, 0)
71-
for i := range args {
72-
tokens = append(tokens, token{name: getArg(args, i), value: getArg(args, i+1)})
78+
func (fi *flagIterator) next() (string, bool) {
79+
if fi.is_empty() {
80+
return "", false
7381
}
7482

75-
return tokens
83+
s := fi.args[fi.index]
84+
fi.index += 1
85+
return s, true
86+
}
87+
88+
func (fi *flagIterator) next_range(n int) ([]string, bool) {
89+
res := []string{}
90+
for i := 0; i < n; i++ {
91+
ch, ok := fi.next()
92+
if !ok {
93+
return res, false
94+
}
95+
res = append(res, ch)
96+
}
97+
return res, true
98+
}
99+
100+
func (fi *flagIterator) is_empty() bool {
101+
return fi.index >= fi.max
76102
}

0 commit comments

Comments
 (0)