153 lines
3.4 KiB
Go
153 lines
3.4 KiB
Go
// Package opt implements command-line flag parsing.
|
|
package opt // import "modernc.org/opt"
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
)
|
|
|
|
type opt struct {
|
|
handler func(opt, arg string) error
|
|
name string
|
|
|
|
arg bool // Enable argument, e.g. `-I foo` or `-I=foo`
|
|
}
|
|
|
|
// A Set represents a set of defined options.
|
|
type Set struct {
|
|
cfg map[string]*opt
|
|
imm []*opt
|
|
}
|
|
|
|
// NewSet returns a new, empty option set.
|
|
func NewSet() *Set { return &Set{cfg: map[string]*opt{}} }
|
|
|
|
// Opt defines a simple option, e.g. `-f`. When the option is found during
|
|
// Parse, the handler is called with the value of the option, e.g. "-f".
|
|
func (p *Set) Opt(name string, handler func(opt string) error) {
|
|
p.cfg[name] = &opt{
|
|
handler: func(opt, arg string) error { return handler(opt) },
|
|
}
|
|
}
|
|
|
|
// Arg defines a simple option with an argument, e.g. `-I foo` or `-I=foo`.
|
|
// Setting imm argument enables additionally `-Ifoo`. When the option is found
|
|
// during Parse, the handler is called with the values of the option and the
|
|
// argument, e.g. "-I" and "foo" for all of the variants.
|
|
func (p *Set) Arg(name string, imm bool, handler func(opt, arg string) error) {
|
|
switch {
|
|
case imm:
|
|
p.imm = append(p.imm, &opt{
|
|
handler: handler,
|
|
name: name,
|
|
})
|
|
default:
|
|
p.cfg[name] = &opt{
|
|
arg: true,
|
|
handler: handler,
|
|
name: name,
|
|
}
|
|
}
|
|
}
|
|
|
|
// Parse parses opts. Must be called after all options are defined. The handler
|
|
// is called for all items in opts that were not defined before using Opt or
|
|
// Arg.
|
|
//
|
|
// If any handler returns a non-nil error, Parse will stop. If the error is of
|
|
// type Skip, the error returned by Parse will contain all the unprocessed
|
|
// items of opts.
|
|
//
|
|
// The opts slice must not be modified by any handler while Parser is
|
|
// executing.
|
|
func (p *Set) Parse(opts []string, handler func(string) error) (err error) {
|
|
defer func() {
|
|
switch err.(type) {
|
|
case Skip:
|
|
err = Skip(opts)
|
|
}
|
|
}()
|
|
|
|
for len(opts) != 0 {
|
|
opt := opts[0]
|
|
opts = opts[1:]
|
|
var arg string
|
|
out:
|
|
switch {
|
|
case strings.HasPrefix(opt, "-"):
|
|
name := opt[1:]
|
|
for _, cfg := range p.imm {
|
|
if strings.HasPrefix(name, cfg.name) {
|
|
switch {
|
|
case name == cfg.name:
|
|
if len(opts) == 0 {
|
|
return fmt.Errorf("missing argument of %s", opt)
|
|
}
|
|
|
|
if err = cfg.handler(opt, opts[0]); err != nil {
|
|
return err
|
|
}
|
|
|
|
opts = opts[1:]
|
|
default:
|
|
if err = cfg.handler(opt[:len(cfg.name)+1], name[len(cfg.name):]); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
break out
|
|
}
|
|
}
|
|
|
|
if n := strings.IndexByte(opt, '='); n > 0 {
|
|
arg = opt[n+1:]
|
|
name = opt[1:n]
|
|
opt = opt[:n]
|
|
}
|
|
switch cfg := p.cfg[name]; {
|
|
case cfg == nil:
|
|
if err = handler(opt); err != nil {
|
|
return err
|
|
}
|
|
default:
|
|
switch {
|
|
case cfg.arg:
|
|
switch {
|
|
case arg != "":
|
|
if err = cfg.handler(opt, arg); err != nil {
|
|
return err
|
|
}
|
|
default:
|
|
if len(opts) == 0 {
|
|
return fmt.Errorf("missing argument of %s", opt)
|
|
}
|
|
|
|
if err = cfg.handler(opt, opts[0]); err != nil {
|
|
return err
|
|
}
|
|
|
|
opts = opts[1:]
|
|
}
|
|
default:
|
|
if err = cfg.handler(opt, ""); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
default:
|
|
if opt == "" {
|
|
break
|
|
}
|
|
|
|
if err = handler(opt); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// Skip is an error that contains all unprocessed items passed to Parse.
|
|
type Skip []string
|
|
|
|
func (s Skip) Error() string { return fmt.Sprint([]string(s)) }
|