Refactor the option parsing code to use methods on a struct
This commit is contained in:
		
							parent
							
								
									34bd7544b1
								
							
						
					
					
						commit
						f4c5adc4c7
					
				
							
								
								
									
										11
									
								
								command.go
								
								
								
								
							
							
						
						
									
										11
									
								
								command.go
								
								
								
								
							|  | @ -1,6 +1,7 @@ | |||
| package cmd | ||||
| 
 | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"os" | ||||
| 	"path/filepath" | ||||
| 
 | ||||
|  | @ -67,14 +68,10 @@ func (c *Command) Execute(args []string) { | |||
| 		args = args[1:] | ||||
| 	} | ||||
| 
 | ||||
| 	restArgs, err := c.opts.Parse(args) | ||||
| 	if err != nil { | ||||
| 		c.ShowHelp() | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	restArgs, err = opts.Globals().Parse(restArgs) | ||||
| 	parser := opts.NewParser(args, c.opts) | ||||
| 	restArgs, err := parser.Parse() | ||||
| 	if err != nil { | ||||
| 		fmt.Printf("Option error: %s\n", err.Error()) | ||||
| 		c.ShowHelp() | ||||
| 		return | ||||
| 	} | ||||
|  |  | |||
|  | @ -1,6 +1,8 @@ | |||
| package opts | ||||
| 
 | ||||
| import "strconv" | ||||
| import ( | ||||
| 	"strconv" | ||||
| ) | ||||
| 
 | ||||
| type BoolOption struct { | ||||
| 	name        string | ||||
|  |  | |||
							
								
								
									
										182
									
								
								opts/parser.go
								
								
								
								
							
							
						
						
									
										182
									
								
								opts/parser.go
								
								
								
								
							|  | @ -2,7 +2,6 @@ package opts | |||
| 
 | ||||
| import ( | ||||
| 	"errors" | ||||
| 	"fmt" | ||||
| 	"strings" | ||||
| ) | ||||
| 
 | ||||
|  | @ -11,133 +10,136 @@ var ( | |||
| 	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 | ||||
| type Parser struct { | ||||
| 	opts Set | ||||
| 	args []string | ||||
| 	curr int | ||||
| } | ||||
| 
 | ||||
| func NewParser(args []string, opts []Option) *Parser { | ||||
| 	return &Parser{ | ||||
| 		opts: append(opts, globalOpts...), | ||||
| 		args: args, | ||||
| 		curr: -1, | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func (p *Parser) Parse() (restArgs []string, err error) { | ||||
| 	for p.hasNext() { | ||||
| 		arg := p.next() | ||||
| 
 | ||||
| 		if !strings.HasPrefix(arg, "-") { // Regular argument
 | ||||
| 			restArgs = append(restArgs, arg) | ||||
| 		} else if arg == "--" { // Options terminator
 | ||||
| 			restArgs = append(restArgs, p.restArgs()...) | ||||
| 			return | ||||
| 		} else if strings.HasPrefix(arg, "--") { // Long option
 | ||||
| 			longName := arg[2:] | ||||
| 			if err = p.parseLongOption(longName); err != nil { | ||||
| 				return | ||||
| 			} | ||||
| 		} else { // Short option
 | ||||
| 			shortNames := arg[1:] | ||||
| 			if err = p.parseShortOption(shortNames); err != nil { | ||||
| 				return | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 		// Handle options terminator
 | ||||
| 		if arg == "--" { | ||||
| 			restArgs = append(restArgs, args[i+1:]...) | ||||
| 	return | ||||
| } | ||||
| 
 | ||||
| 		// Handle long option
 | ||||
| 		if strings.HasPrefix(arg, "--") { | ||||
| 			longName := arg[2:] | ||||
| func (p *Parser) hasNext() bool { | ||||
| 	return (p.curr + 1) < len(p.args) | ||||
| } | ||||
| 
 | ||||
| func (p *Parser) next() string { | ||||
| 	p.curr++ | ||||
| 
 | ||||
| 	if p.curr >= len(p.args) { | ||||
| 		return "" | ||||
| 	} | ||||
| 
 | ||||
| 	return p.args[p.curr] | ||||
| } | ||||
| 
 | ||||
| func (p *Parser) value() string { | ||||
| 	p.curr++ | ||||
| 
 | ||||
| 	if p.curr >= len(p.args) { | ||||
| 		return "" | ||||
| 	} | ||||
| 
 | ||||
| 	arg := p.args[p.curr] | ||||
| 	return arg | ||||
| } | ||||
| 
 | ||||
| func (p *Parser) restArgs() []string { | ||||
| 	rest := p.args[p.curr+1:] | ||||
| 	p.curr = len(p.args) | ||||
| 
 | ||||
| 	return rest | ||||
| } | ||||
| 
 | ||||
| func (p *Parser) parseLongOption(longName string) error { | ||||
| 	value := "" | ||||
| 
 | ||||
| 	equals := strings.Index(longName, "=") | ||||
| 			if equals > 0 { | ||||
| 	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++ | ||||
| 	} else { | ||||
| 		value = p.value() | ||||
| 	} | ||||
| 
 | ||||
| 			parsed := false | ||||
| 			for _, opt := range s { | ||||
| 				if opt.Name() != longName { | ||||
| 					continue | ||||
| 	opt, ok := p.opts.GetByLongName(longName) | ||||
| 	if ok { | ||||
| 		if err := opt.Parse(value); err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 				if err = opt.Parse(value); err != nil { | ||||
| 					return | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| 				parsed = true | ||||
| 				break | ||||
| 			} | ||||
| 
 | ||||
| 			if !parsed { | ||||
| 				restArgs = append(restArgs, arg) | ||||
| 			} | ||||
| 
 | ||||
| 			continue | ||||
| 		} | ||||
| 
 | ||||
| 		// Handle short option
 | ||||
| 		shortNames := arg[1:] | ||||
| func (p *Parser) parseShortOption(shortNames string) error { | ||||
| 	if len(shortNames) == 0 { | ||||
| 			err = ErrInvalidShortOption | ||||
| 		return ErrInvalidShortOption | ||||
| 	} else if len(shortNames) == 1 { | ||||
| 			parsed := false | ||||
| 			value := "" | ||||
| 			if i < len(args)-1 && !strings.HasPrefix(args[i+1], "-") { | ||||
| 				value = args[i+1] | ||||
| 				i++ | ||||
| 			} | ||||
| 		value := p.value() | ||||
| 
 | ||||
| 			for _, opt := range s { | ||||
| 				if opt.ShortName() != shortNames { | ||||
| 					continue | ||||
| 		opt, ok := p.opts.GetByShortName(shortNames) | ||||
| 		if ok { | ||||
| 			if err := opt.Parse(value); err != nil { | ||||
| 				return err | ||||
| 			} | ||||
| 
 | ||||
| 				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 | ||||
| 					} | ||||
| 
 | ||||
| 			opt, ok := p.opts.GetByShortName(string(shortName)) | ||||
| 			if ok { | ||||
| 				if opt.TakesArg() { | ||||
| 					if j > 0 { | ||||
| 							err = ErrCannotChainOption | ||||
| 							return | ||||
| 						return ErrCannotChainOption | ||||
| 					} | ||||
| 
 | ||||
| 						takesValue = true | ||||
| 						value = shortNames[j+1:] | ||||
| 					value = shortNames[1:] | ||||
| 					value = strings.TrimPrefix(value, "=") | ||||
| 						if len(value) == 0 && i < len(args)-1 && !strings.HasPrefix(args[i+1], "-") { | ||||
| 							value = args[i+1] | ||||
| 							i++ | ||||
| 					if len(value) == 0 { | ||||
| 						value = p.value() | ||||
| 					} | ||||
| 					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)) | ||||
| 					} | ||||
| 				if err := opt.Parse(value); err != nil { | ||||
| 					return err | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	return | ||||
| 	return nil | ||||
| } | ||||
|  |  | |||
							
								
								
									
										18
									
								
								opts/set.go
								
								
								
								
							
							
						
						
									
										18
									
								
								opts/set.go
								
								
								
								
							|  | @ -33,6 +33,24 @@ func (s Set) Get(name string) (Option, bool) { | |||
| 	return nil, false | ||||
| } | ||||
| 
 | ||||
| func (s Set) GetByLongName(longName string) (Option, bool) { | ||||
| 	for _, o := range s { | ||||
| 		if o.Name() == longName { | ||||
| 			return o, true | ||||
| 		} | ||||
| 	} | ||||
| 	return nil, false | ||||
| } | ||||
| 
 | ||||
| func (s Set) GetByShortName(shortName string) (Option, bool) { | ||||
| 	for _, o := range s { | ||||
| 		if o.ShortName() == shortName { | ||||
| 			return o, true | ||||
| 		} | ||||
| 	} | ||||
| 	return nil, false | ||||
| } | ||||
| 
 | ||||
| func (s Set) GetBool(name string) (*BoolOption, bool) { | ||||
| 	o, ok := s.Get(name) | ||||
| 	if !ok { | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue