summaryrefslogtreecommitdiffstats
path: root/vendor/modernc.org/opt/opt.go
blob: b4bd8282bac1489e8d9b4403f9135a1e5d31f08d (plain) (blame)
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
// 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]
		opt0 := opt
		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:
						opt = opt[:len(cfg.name)+1]
						val := strings.TrimPrefix(name[len(cfg.name):], "=")
						if err = cfg.handler(opt, val); 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(opt0); 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)) }