Add option parsing
This commit is contained in:
parent
9079086626
commit
34bd7544b1
29
command.go
29
command.go
|
@ -67,27 +67,34 @@ func (c *Command) Execute(args []string) {
|
|||
args = args[1:]
|
||||
}
|
||||
|
||||
if err := c.opts.Parse(args); err != nil {
|
||||
restArgs, err := c.opts.Parse(args)
|
||||
if err != nil {
|
||||
c.ShowHelp()
|
||||
return
|
||||
}
|
||||
|
||||
if len(args) > 0 {
|
||||
sc, ok := c.subcommands.Get(args[0])
|
||||
if ok {
|
||||
sc.Execute(args[1:])
|
||||
return
|
||||
}
|
||||
restArgs, err = opts.Globals().Parse(restArgs)
|
||||
if err != nil {
|
||||
c.ShowHelp()
|
||||
return
|
||||
}
|
||||
|
||||
// TODO: remove when done with option parsing
|
||||
if args[0] == "--help" {
|
||||
c.ShowHelp()
|
||||
if len(restArgs) > 0 {
|
||||
sc, ok := c.subcommands.Get(restArgs[0])
|
||||
if ok {
|
||||
sc.Execute(restArgs[1:])
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
helpOpt, ok := opts.Globals().GetBool("help")
|
||||
if ok && helpOpt.Value() {
|
||||
c.ShowHelp()
|
||||
return
|
||||
}
|
||||
|
||||
if c.CanRun() {
|
||||
c.Run(args)
|
||||
c.Run(restArgs)
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@ type BoolOption struct {
|
|||
value bool
|
||||
}
|
||||
|
||||
var _ Option = (*StringOption)(nil)
|
||||
var _ Option = (*BoolOption)(nil)
|
||||
|
||||
func Bool(name, shortName string, defaultValue bool, description string) *BoolOption {
|
||||
return &BoolOption{
|
||||
|
@ -39,6 +39,7 @@ func (o *BoolOption) ShortName() string {
|
|||
// Value implements Option.
|
||||
func (o *BoolOption) Parse(raw string) error {
|
||||
if raw == "" {
|
||||
o.value = !o.value
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -51,6 +52,11 @@ func (o *BoolOption) Parse(raw string) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// TakesArg implements Option.
|
||||
func (*BoolOption) TakesArg() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (o *BoolOption) Value() bool {
|
||||
return o.value
|
||||
}
|
||||
|
|
|
@ -51,6 +51,11 @@ func (o *FloatOption) Parse(raw string) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// TakesArg implements Option.
|
||||
func (*FloatOption) TakesArg() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (o *FloatOption) Value() float64 {
|
||||
return o.value
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@ type IntOption struct {
|
|||
value int
|
||||
}
|
||||
|
||||
var _ Option = (*StringOption)(nil)
|
||||
var _ Option = (*IntOption)(nil)
|
||||
|
||||
func Int(name, shortName string, defaultValue int, description string) *IntOption {
|
||||
return &IntOption{
|
||||
|
@ -51,6 +51,11 @@ func (o *IntOption) Parse(raw string) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// TakesArg implements Option.
|
||||
func (*IntOption) TakesArg() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (o *IntOption) Value() int {
|
||||
return o.value
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ type Option interface {
|
|||
ShortName() string
|
||||
Description() string
|
||||
Parse(raw string) error
|
||||
TakesArg() bool
|
||||
}
|
||||
|
||||
func Names(o Option) string {
|
||||
|
|
|
@ -0,0 +1,143 @@
|
|||
package opts
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrInvalidShortOption = errors.New("invalid short option")
|
||||
ErrCannotChainOption = errors.New("cannot chain option as it takes an argument")
|
||||
)
|
||||
|
||||
func (s Set) Parse(args []string) (restArgs []string, err error) {
|
||||
for i := 0; i < len(args); i++ {
|
||||
arg := args[i]
|
||||
|
||||
// Handle regular argument
|
||||
if !strings.HasPrefix(arg, "-") {
|
||||
restArgs = append(restArgs, arg)
|
||||
continue
|
||||
}
|
||||
|
||||
// Handle options terminator
|
||||
if arg == "--" {
|
||||
restArgs = append(restArgs, args[i+1:]...)
|
||||
return
|
||||
}
|
||||
|
||||
// Handle long option
|
||||
if strings.HasPrefix(arg, "--") {
|
||||
longName := arg[2:]
|
||||
value := ""
|
||||
|
||||
equals := strings.Index(longName, "=")
|
||||
if equals > 0 {
|
||||
longName = longName[:equals]
|
||||
value = longName[equals+1:]
|
||||
} else if i < len(args)-1 && !strings.HasPrefix(args[i+1], "-") {
|
||||
value = args[i+1]
|
||||
i++
|
||||
}
|
||||
|
||||
parsed := false
|
||||
for _, opt := range s {
|
||||
if opt.Name() != longName {
|
||||
continue
|
||||
}
|
||||
|
||||
if err = opt.Parse(value); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
parsed = true
|
||||
break
|
||||
}
|
||||
|
||||
if !parsed {
|
||||
restArgs = append(restArgs, arg)
|
||||
}
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
// Handle short option
|
||||
shortNames := arg[1:]
|
||||
if len(shortNames) == 0 {
|
||||
err = ErrInvalidShortOption
|
||||
} else if len(shortNames) == 1 {
|
||||
parsed := false
|
||||
value := ""
|
||||
if i < len(args)-1 && !strings.HasPrefix(args[i+1], "-") {
|
||||
value = args[i+1]
|
||||
i++
|
||||
}
|
||||
|
||||
for _, opt := range s {
|
||||
if opt.ShortName() != shortNames {
|
||||
continue
|
||||
}
|
||||
|
||||
if err = opt.Parse(value); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
parsed = true
|
||||
break
|
||||
}
|
||||
|
||||
if !parsed {
|
||||
restArgs = append(restArgs, arg)
|
||||
}
|
||||
} else {
|
||||
for j := 0; j < len(shortNames); j++ {
|
||||
shortName := shortNames[j]
|
||||
value := ""
|
||||
takesValue := false
|
||||
parsed := false
|
||||
|
||||
for _, opt := range s {
|
||||
if opt.ShortName() != string(shortName) {
|
||||
continue
|
||||
}
|
||||
|
||||
if opt.TakesArg() {
|
||||
if j > 0 {
|
||||
err = ErrCannotChainOption
|
||||
return
|
||||
}
|
||||
|
||||
takesValue = true
|
||||
value = shortNames[j+1:]
|
||||
value = strings.TrimPrefix(value, "=")
|
||||
if len(value) == 0 && i < len(args)-1 && !strings.HasPrefix(args[i+1], "-") {
|
||||
value = args[i+1]
|
||||
i++
|
||||
}
|
||||
j = len(shortNames)
|
||||
}
|
||||
|
||||
parsed = true
|
||||
|
||||
if err = opt.Parse(value); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
break
|
||||
}
|
||||
|
||||
if !parsed {
|
||||
if takesValue {
|
||||
restArgs = append(restArgs, arg)
|
||||
break
|
||||
} else {
|
||||
restArgs = append(restArgs, fmt.Sprintf("-%c", shortName))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
91
opts/set.go
91
opts/set.go
|
@ -6,23 +6,6 @@ func NewSet() Set {
|
|||
return Set{}
|
||||
}
|
||||
|
||||
func (s *Set) Add(f Option) {
|
||||
*s = append(*s, f)
|
||||
}
|
||||
|
||||
func (s Set) Get(name string) (Option, bool) {
|
||||
for _, f := range s {
|
||||
if f.Name() == name {
|
||||
return f, true
|
||||
}
|
||||
|
||||
if f.ShortName() == name {
|
||||
return f, true
|
||||
}
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
func (s Set) MaxWidth() int {
|
||||
max := 0
|
||||
for _, f := range s {
|
||||
|
@ -33,7 +16,75 @@ func (s Set) MaxWidth() int {
|
|||
return max
|
||||
}
|
||||
|
||||
// TODO: Implement
|
||||
func (s Set) Parse(args []string) error {
|
||||
return nil
|
||||
func (s *Set) Add(f Option) {
|
||||
*s = append(*s, f)
|
||||
}
|
||||
|
||||
func (s Set) Get(name string) (Option, bool) {
|
||||
for _, o := range s {
|
||||
if o.Name() == name {
|
||||
return o, true
|
||||
}
|
||||
|
||||
if o.ShortName() == name {
|
||||
return o, true
|
||||
}
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
func (s Set) GetBool(name string) (*BoolOption, bool) {
|
||||
o, ok := s.Get(name)
|
||||
if !ok {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
b, ok := o.(*BoolOption)
|
||||
if !ok {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
return b, true
|
||||
}
|
||||
|
||||
func (s Set) GetString(name string) (*StringOption, bool) {
|
||||
o, ok := s.Get(name)
|
||||
if !ok {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
b, ok := o.(*StringOption)
|
||||
if !ok {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
return b, true
|
||||
}
|
||||
|
||||
func (s Set) GetInt(name string) (*IntOption, bool) {
|
||||
o, ok := s.Get(name)
|
||||
if !ok {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
b, ok := o.(*IntOption)
|
||||
if !ok {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
return b, true
|
||||
}
|
||||
|
||||
func (s Set) GetFloat(name string) (*FloatOption, bool) {
|
||||
o, ok := s.Get(name)
|
||||
if !ok {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
b, ok := o.(*FloatOption)
|
||||
if !ok {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
return b, true
|
||||
}
|
||||
|
|
|
@ -43,6 +43,11 @@ func (o *StringOption) Parse(raw string) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// TakesArg implements Option.
|
||||
func (*StringOption) TakesArg() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (o *StringOption) Value() string {
|
||||
return o.value
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue