gtsocial-umbx

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

opt.go (3578B)


      1 // Package opt implements command-line flag parsing.
      2 package opt // import "modernc.org/opt"
      3 
      4 import (
      5 	"fmt"
      6 	"strings"
      7 )
      8 
      9 type opt struct {
     10 	handler func(opt, arg string) error
     11 	name    string
     12 
     13 	arg bool // Enable argument, e.g. `-I foo` or `-I=foo`
     14 }
     15 
     16 // A Set represents a set of defined options.
     17 type Set struct {
     18 	cfg map[string]*opt
     19 	imm []*opt
     20 }
     21 
     22 // NewSet returns a new, empty option set.
     23 func NewSet() *Set { return &Set{cfg: map[string]*opt{}} }
     24 
     25 // Opt defines a simple option, e.g. `-f`. When the option is found during
     26 // Parse, the handler is called with the value of the option, e.g. "-f".
     27 func (p *Set) Opt(name string, handler func(opt string) error) {
     28 	p.cfg[name] = &opt{
     29 		handler: func(opt, arg string) error { return handler(opt) },
     30 	}
     31 }
     32 
     33 // Arg defines a simple option with an argument, e.g. `-I foo` or `-I=foo`.
     34 // Setting imm argument enables additionally `-Ifoo`. When the option is found
     35 // during Parse, the handler is called with the values of the option and the
     36 // argument, e.g. "-I" and "foo" for all of the variants.
     37 func (p *Set) Arg(name string, imm bool, handler func(opt, arg string) error) {
     38 	switch {
     39 	case imm:
     40 		p.imm = append(p.imm, &opt{
     41 			handler: handler,
     42 			name:    name,
     43 		})
     44 	default:
     45 		p.cfg[name] = &opt{
     46 			arg:     true,
     47 			handler: handler,
     48 			name:    name,
     49 		}
     50 	}
     51 }
     52 
     53 // Parse parses opts. Must be called after all options are defined. The handler
     54 // is called for all items in opts that were not defined before using Opt or
     55 // Arg.
     56 //
     57 // If any handler returns a non-nil error, Parse will stop.  If the error is of
     58 // type Skip, the error returned by Parse will contain all the unprocessed
     59 // items of opts.
     60 //
     61 // The opts slice must not be modified by any handler while Parser is
     62 // executing.
     63 func (p *Set) Parse(opts []string, handler func(string) error) (err error) {
     64 	defer func() {
     65 		switch err.(type) {
     66 		case Skip:
     67 			err = Skip(opts)
     68 		}
     69 	}()
     70 
     71 	for len(opts) != 0 {
     72 		opt := opts[0]
     73 		opt0 := opt
     74 		opts = opts[1:]
     75 		var arg string
     76 	out:
     77 		switch {
     78 		case strings.HasPrefix(opt, "-"):
     79 			name := opt[1:]
     80 			for _, cfg := range p.imm {
     81 				if strings.HasPrefix(name, cfg.name) {
     82 					switch {
     83 					case name == cfg.name:
     84 						if len(opts) == 0 {
     85 							return fmt.Errorf("missing argument of %s", opt)
     86 						}
     87 
     88 						if err = cfg.handler(opt, opts[0]); err != nil {
     89 							return err
     90 						}
     91 
     92 						opts = opts[1:]
     93 					default:
     94 						opt = opt[:len(cfg.name)+1]
     95 						val := strings.TrimPrefix(name[len(cfg.name):], "=")
     96 						if err = cfg.handler(opt, val); err != nil {
     97 							return err
     98 						}
     99 					}
    100 					break out
    101 				}
    102 			}
    103 
    104 			if n := strings.IndexByte(opt, '='); n > 0 {
    105 				arg = opt[n+1:]
    106 				name = opt[1:n]
    107 				opt = opt[:n]
    108 			}
    109 			switch cfg := p.cfg[name]; {
    110 			case cfg == nil:
    111 				if err = handler(opt0); err != nil {
    112 					return err
    113 				}
    114 			default:
    115 				switch {
    116 				case cfg.arg:
    117 					switch {
    118 					case arg != "":
    119 						if err = cfg.handler(opt, arg); err != nil {
    120 							return err
    121 						}
    122 					default:
    123 						if len(opts) == 0 {
    124 							return fmt.Errorf("missing argument of %s", opt)
    125 						}
    126 
    127 						if err = cfg.handler(opt, opts[0]); err != nil {
    128 							return err
    129 						}
    130 
    131 						opts = opts[1:]
    132 					}
    133 				default:
    134 					if err = cfg.handler(opt, ""); err != nil {
    135 						return err
    136 					}
    137 				}
    138 			}
    139 		default:
    140 			if opt == "" {
    141 				break
    142 			}
    143 
    144 			if err = handler(opt); err != nil {
    145 				return err
    146 			}
    147 		}
    148 	}
    149 	return nil
    150 }
    151 
    152 // Skip is an error that contains all unprocessed items passed to Parse.
    153 type Skip []string
    154 
    155 func (s Skip) Error() string { return fmt.Sprint([]string(s)) }