| 
									
										
										
										
											2021-08-29 15:41:41 +01:00
										 |  |  | // 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] | 
					
						
							| 
									
										
										
										
											2022-05-02 14:05:18 +01:00
										 |  |  | 		opt0 := opt | 
					
						
							| 
									
										
										
										
											2021-08-29 15:41:41 +01:00
										 |  |  | 		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: | 
					
						
							| 
									
										
										
										
											2022-05-02 14:05:18 +01:00
										 |  |  | 						opt = opt[:len(cfg.name)+1] | 
					
						
							|  |  |  | 						val := strings.TrimPrefix(name[len(cfg.name):], "=") | 
					
						
							|  |  |  | 						if err = cfg.handler(opt, val); err != nil { | 
					
						
							| 
									
										
										
										
											2021-08-29 15:41:41 +01:00
										 |  |  | 							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: | 
					
						
							| 
									
										
										
										
											2022-05-02 14:05:18 +01:00
										 |  |  | 				if err = handler(opt0); err != nil { | 
					
						
							| 
									
										
										
										
											2021-08-29 15:41:41 +01:00
										 |  |  | 					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)) } |