forked from cloudfoundry/multiapps-cli-plugin
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathflags_parser.go
More file actions
194 lines (161 loc) · 5.58 KB
/
flags_parser.go
File metadata and controls
194 lines (161 loc) · 5.58 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
package commands
import (
"errors"
"flag"
"fmt"
"strings"
"github.com/cloudfoundry-incubator/multiapps-cli-plugin/log"
)
// CommandFlagsParser used for parsing the arguments
type CommandFlagsParser struct {
flag *flag.FlagSet
parser FlagsParser
validator FlagsValidator
parsedArgs []string
}
// NewCommandFlagsParser creates new command flags parser
func NewCommandFlagsParser(flag *flag.FlagSet, parser FlagsParser, validator FlagsValidator) CommandFlagsParser {
return CommandFlagsParser{flag: flag, parser: parser, validator: validator, parsedArgs: make([]string, 0)}
}
// Parse parsing the args
func (p *CommandFlagsParser) Parse(args []string) error {
err := p.parser.ParseFlags(p.flag, args)
if err != nil {
return err
}
// assume that the parsing of arguments is successful - determine the arguments which are not flagged
p.parsedArgs = determineParsedNotFlaggedArguments(p.flag, args)
return p.validator.ValidateParsedFlags(p.flag)
}
func determineParsedNotFlaggedArguments(flag *flag.FlagSet, args []string) []string {
result := make([]string, 0)
for _, arg := range args {
if argument := flag.Lookup(strings.Replace(arg, "-", "", 2)); argument == nil {
result = append(result, arg)
} else {
break
}
}
return result
}
// Args returns the first parsed command line arguments WITHOUT the options
func (p CommandFlagsParser) Args() []string {
return p.parsedArgs
}
// FlagsParser interface used for parsing the command line arguments using the flag library
type FlagsParser interface {
ParseFlags(flags *flag.FlagSet, args []string) error
}
// FlagsValidator interface used for validating the parsed flags
type FlagsValidator interface {
ValidateParsedFlags(flags *flag.FlagSet) error
}
// DefaultCommandFlagsParser defines default implementation of the parser. It uses positional arguments and assumes that the command args will contain arguments
type DefaultCommandFlagsParser struct {
positionalArgNames []string
}
// NewDefaultCommandFlagsParser initializes DefaultCommandFlagsParser
func NewDefaultCommandFlagsParser(positionalArgNames []string) DefaultCommandFlagsParser {
return DefaultCommandFlagsParser{positionalArgNames: positionalArgNames}
}
// ParseFlags see DefaultCommandFlagsParser
func (p DefaultCommandFlagsParser) ParseFlags(flags *flag.FlagSet, args []string) error {
// Check for missing positional arguments
positionalArgsCount := len(p.positionalArgNames)
if len(args) < positionalArgsCount {
return fmt.Errorf("Missing positional argument %q", p.positionalArgNames[len(args)])
}
for i := 0; i < positionalArgsCount; i++ {
if flags.Lookup(strings.Replace(args[i], "-", "", 1)) != nil {
return fmt.Errorf("Missing positional argument %q", p.positionalArgNames[i])
}
}
// Parse the arguments
err := flags.Parse(args[positionalArgsCount:])
if err != nil {
if unknownFlags := collectUnknownFlags(flags, args); len(unknownFlags) > 0 {
return fmt.Errorf("Unknown or wrong flags: %s", strings.Join(unknownFlags, ", "))
} else {
return errors.New("Parsing of arguments has failed")
}
}
// Check for wrong arguments
if flags.NArg() > 0 {
return errors.New("Wrong arguments")
}
return nil
}
// DefaultCommandFlagsValidator default implementation of the FlagValidator
type DefaultCommandFlagsValidator struct {
requiredFlags map[string]bool
}
// NewDefaultCommandFlagsValidator creates a default validator for flags
func NewDefaultCommandFlagsValidator(requiredFlags map[string]bool) DefaultCommandFlagsValidator {
return DefaultCommandFlagsValidator{requiredFlags: requiredFlags}
}
// ValidateParsedFlags uses a required flags map in order to validate whether the arguments are valid
func (v DefaultCommandFlagsValidator) ValidateParsedFlags(flags *flag.FlagSet) error {
var missingRequiredOptions []string
// Check for missing required flags
flags.VisitAll(func(f *flag.Flag) {
log.Traceln(f.Name, f.Value)
if v.requiredFlags[f.Name] && f.Value.String() == "" {
missingRequiredOptions = append(missingRequiredOptions, f.Name)
}
})
if len(missingRequiredOptions) != 0 {
return fmt.Errorf("Missing required options '%v'", missingRequiredOptions)
}
return nil
}
func collectUnknownFlags(flags *flag.FlagSet, args []string) []string {
var unknownFlags []string
alreadySeenUnknownFLags := make(map[string]int)
for i := 0; i < len(args); i++ {
currentArgument := args[i]
if !strings.HasPrefix(currentArgument, "-") {
continue
}
currentFlag := currentArgument
flagName := strings.TrimLeft(currentFlag, "-")
if flagName == "" {
continue
}
isFlagKnown := flags.Lookup(flagName)
if isFlagKnown != nil {
isBoolean := isBoolFlag(isFlagKnown)
if !isBoolean {
i = tryToGetNext(args, i)
}
continue
}
appendOnlyWhenCountIsOne(alreadySeenUnknownFLags, currentFlag, &unknownFlags)
i = tryToGetNext(args, i)
}
return unknownFlags
}
func isBoolFlag(flag *flag.Flag) bool {
type boolFlagInterface interface{ IsBoolFlag() bool }
boolFlag, isInterfaceImplemented := flag.Value.(boolFlagInterface)
if !isInterfaceImplemented {
return false
}
return boolFlag.IsBoolFlag()
}
func tryToGetNext(args []string, currentIndex int) int {
nextIndex := currentIndex + 1
if nextIndex < len(args) {
nextArgument := args[nextIndex]
nextHasPrefixDash := strings.HasPrefix(nextArgument, "-")
if !nextHasPrefixDash {
return nextIndex
}
}
return currentIndex
}
func appendOnlyWhenCountIsOne(alreadySeenUnknownFLags map[string]int, currentFlag string, unknownFlags *[]string) {
alreadySeenUnknownFLags[currentFlag]++
if alreadySeenUnknownFLags[currentFlag] == 1 {
*unknownFlags = append(*unknownFlags, currentFlag)
}
}