Add option parsing
This commit is contained in:
parent
9079086626
commit
34bd7544b1
23
command.go
23
command.go
|
@ -67,27 +67,34 @@ func (c *Command) Execute(args []string) {
|
||||||
args = args[1:]
|
args = args[1:]
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := c.opts.Parse(args); err != nil {
|
restArgs, err := c.opts.Parse(args)
|
||||||
|
if err != nil {
|
||||||
c.ShowHelp()
|
c.ShowHelp()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(args) > 0 {
|
restArgs, err = opts.Globals().Parse(restArgs)
|
||||||
sc, ok := c.subcommands.Get(args[0])
|
if err != nil {
|
||||||
|
c.ShowHelp()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(restArgs) > 0 {
|
||||||
|
sc, ok := c.subcommands.Get(restArgs[0])
|
||||||
if ok {
|
if ok {
|
||||||
sc.Execute(args[1:])
|
sc.Execute(restArgs[1:])
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: remove when done with option parsing
|
helpOpt, ok := opts.Globals().GetBool("help")
|
||||||
if args[0] == "--help" {
|
if ok && helpOpt.Value() {
|
||||||
c.ShowHelp()
|
c.ShowHelp()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if c.CanRun() {
|
if c.CanRun() {
|
||||||
c.Run(args)
|
c.Run(restArgs)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -9,7 +9,7 @@ type BoolOption struct {
|
||||||
value bool
|
value bool
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ Option = (*StringOption)(nil)
|
var _ Option = (*BoolOption)(nil)
|
||||||
|
|
||||||
func Bool(name, shortName string, defaultValue bool, description string) *BoolOption {
|
func Bool(name, shortName string, defaultValue bool, description string) *BoolOption {
|
||||||
return &BoolOption{
|
return &BoolOption{
|
||||||
|
@ -39,6 +39,7 @@ func (o *BoolOption) ShortName() string {
|
||||||
// Value implements Option.
|
// Value implements Option.
|
||||||
func (o *BoolOption) Parse(raw string) error {
|
func (o *BoolOption) Parse(raw string) error {
|
||||||
if raw == "" {
|
if raw == "" {
|
||||||
|
o.value = !o.value
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -51,6 +52,11 @@ func (o *BoolOption) Parse(raw string) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TakesArg implements Option.
|
||||||
|
func (*BoolOption) TakesArg() bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
func (o *BoolOption) Value() bool {
|
func (o *BoolOption) Value() bool {
|
||||||
return o.value
|
return o.value
|
||||||
}
|
}
|
||||||
|
|
|
@ -51,6 +51,11 @@ func (o *FloatOption) Parse(raw string) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TakesArg implements Option.
|
||||||
|
func (*FloatOption) TakesArg() bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
func (o *FloatOption) Value() float64 {
|
func (o *FloatOption) Value() float64 {
|
||||||
return o.value
|
return o.value
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,7 +9,7 @@ type IntOption struct {
|
||||||
value int
|
value int
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ Option = (*StringOption)(nil)
|
var _ Option = (*IntOption)(nil)
|
||||||
|
|
||||||
func Int(name, shortName string, defaultValue int, description string) *IntOption {
|
func Int(name, shortName string, defaultValue int, description string) *IntOption {
|
||||||
return &IntOption{
|
return &IntOption{
|
||||||
|
@ -51,6 +51,11 @@ func (o *IntOption) Parse(raw string) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TakesArg implements Option.
|
||||||
|
func (*IntOption) TakesArg() bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
func (o *IntOption) Value() int {
|
func (o *IntOption) Value() int {
|
||||||
return o.value
|
return o.value
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,7 @@ type Option interface {
|
||||||
ShortName() string
|
ShortName() string
|
||||||
Description() string
|
Description() string
|
||||||
Parse(raw string) error
|
Parse(raw string) error
|
||||||
|
TakesArg() bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func Names(o Option) string {
|
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{}
|
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 {
|
func (s Set) MaxWidth() int {
|
||||||
max := 0
|
max := 0
|
||||||
for _, f := range s {
|
for _, f := range s {
|
||||||
|
@ -33,7 +16,75 @@ func (s Set) MaxWidth() int {
|
||||||
return max
|
return max
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Implement
|
func (s *Set) Add(f Option) {
|
||||||
func (s Set) Parse(args []string) error {
|
*s = append(*s, f)
|
||||||
return nil
|
}
|
||||||
|
|
||||||
|
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
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TakesArg implements Option.
|
||||||
|
func (*StringOption) TakesArg() bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
func (o *StringOption) Value() string {
|
func (o *StringOption) Value() string {
|
||||||
return o.value
|
return o.value
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue