github.com/GeniusesGroup/libgo@v0.0.0-20220929090155-5ff932cb408e/command/flag.go (about)

     1  /* For license and copyright information please see the LEGAL file in the code repository */
     2  
     3  package cmd
     4  
     5  import (
     6  	"strings"
     7  
     8  	"github.com/GeniusesGroup/libgo/protocol"
     9  )
    10  
    11  // A FlagSet represents a set of defined fields
    12  //
    13  // field names must be unique within a FlagSet. An attempt to define a flag whose
    14  // name is already in use will cause a panic.
    15  type FlagSet struct {
    16  	fields []protocol.Field
    17  	parsed []protocol.Field
    18  	args   []string // arguments after flags
    19  }
    20  
    21  // argument list should not include the commands name.
    22  func (f *FlagSet) Init(fields []protocol.Field, arguments []string) {
    23  	f.fields = fields
    24  	f.checkFields()
    25  	if f.parsed == nil {
    26  		f.parsed = make([]protocol.Field, 0, len(f.fields))
    27  	}
    28  	f.args = arguments
    29  }
    30  func (f *FlagSet) Reinit() {
    31  	f.fields = nil
    32  	f.parsed = f.parsed[:0]
    33  	f.args = nil
    34  }
    35  func (f *FlagSet) Deinit() { f.Reinit() }
    36  
    37  // Parse parses flag definitions from f.args.
    38  // It Must be called after Init called and before flags are accessed by the program.
    39  func (f *FlagSet) Parse() (err protocol.Error) {
    40  	for len(f.args) > 0 {
    41  		err = f.parseOne()
    42  		if err != nil {
    43  			return
    44  		}
    45  	}
    46  	return
    47  }
    48  
    49  // NFlag returns the number of flags that have been set.
    50  func (f *FlagSet) NFlag() int { return len(f.fields) }
    51  
    52  // NArg is the number of arguments remaining after flags have been processed.
    53  func (f *FlagSet) NArg() int { return len(f.args) }
    54  
    55  // Args returns the non-flag arguments.
    56  func (f *FlagSet) Args() []string { return f.args }
    57  
    58  // Arg returns the i'th argument. Arg(0) is the first remaining argument
    59  // after flags have been processed. Arg returns an empty string if the
    60  // requested element does not exist.
    61  func (f *FlagSet) Arg(i int) string {
    62  	if i < 0 || i >= len(f.args) {
    63  		return ""
    64  	}
    65  	return f.args[i]
    66  }
    67  
    68  // VisitAll visits the flags in given order, calling fn for each.
    69  // It visits all flags, even those not set.
    70  func (f *FlagSet) VisitAll(fn func(protocol.Field)) {
    71  	for _, flag := range f.fields {
    72  		fn(flag)
    73  	}
    74  }
    75  
    76  // Visit visits the flags in given order, calling fn for each.
    77  // It visits only those flags that have been set.
    78  func (f *FlagSet) Visit(fn func(protocol.Field)) {
    79  	for _, field := range f.parsed {
    80  		fn(field)
    81  	}
    82  }
    83  
    84  // Lookup returns the Field of the named flag, returning nil if none exists.
    85  func (f *FlagSet) Lookup(name string) protocol.Field {
    86  	for _, field := range f.fields {
    87  		if field.Name() == name || field.Abbreviation() == name {
    88  			return field
    89  		}
    90  	}
    91  	return nil
    92  }
    93  
    94  // Set sets the value of the named flag.
    95  func (f *FlagSet) Set(name, value string) (err protocol.Error) {
    96  	var flag = f.Lookup(name)
    97  	if flag == nil {
    98  		return &ErrFlagNotFound
    99  	}
   100  
   101  	var hasValue = len(value) > 0
   102  
   103  	if flag.Type() == protocol.FieldType_Boolean && !hasValue { // special case: doesn't need an arg
   104  		value = "true"
   105  	} else {
   106  		// It must have a value, which might be the next argument.
   107  		if !hasValue && len(f.args) > 0 {
   108  			// value is the next arg
   109  			hasValue = true
   110  			value, f.args = f.args[0], f.args[1:]
   111  		}
   112  		if !hasValue {
   113  			return &ErrFlagNeedsAnArgument
   114  		}
   115  	}
   116  
   117  	err = flag.FromString(value)
   118  	if err != nil {
   119  		return
   120  	}
   121  	f.parsed = append(f.parsed, flag)
   122  	return nil
   123  }
   124  
   125  func (f *FlagSet) checkFields() {
   126  	var fieldsName = make([]string, 0, len(f.fields))
   127  
   128  	for _, field := range f.fields {
   129  		var fieldName = field.Name()
   130  
   131  		// Flag must not begin "-" or contain "=".
   132  		if strings.HasPrefix(fieldName, "-") {
   133  			panic(fieldName + " flag begins with -")
   134  		} else if strings.Contains(fieldName, "=") {
   135  			panic(fieldName + " flag %q contains =")
   136  		}
   137  
   138  		fieldsName = append(fieldsName, fieldName)
   139  	}
   140  
   141  	for i := 0; i < len(fieldsName); i++ {
   142  		for j := i + 1; j < len(fieldsName); j++ {
   143  			var fieldName = fieldsName[i]
   144  			if fieldName == fieldsName[j] {
   145  				// Happens only if flags are declared with identical names
   146  				if fieldName == "" {
   147  					panic(fieldName + " flag redefined.")
   148  				} else {
   149  					panic(fieldName + "flag redefined as" + fieldsName[j])
   150  				}
   151  			}
   152  		}
   153  	}
   154  }
   155  
   156  // parseOne parses one flag
   157  func (f *FlagSet) parseOne() (err protocol.Error) {
   158  	if len(f.args) == 0 {
   159  		return nil
   160  	}
   161  
   162  	var s = f.args[0]
   163  	if len(s) < 2 || s[0] != '-' {
   164  		return &ErrFlagBadSyntax
   165  	}
   166  	var numMinuses = 1
   167  	if s[1] == '-' {
   168  		numMinuses++
   169  		if len(s) == 2 { // "--" terminates the flags
   170  			f.args = f.args[1:]
   171  			return &ErrFlagBadSyntax
   172  		}
   173  	}
   174  	var name = s[numMinuses:]
   175  	if len(name) == 0 || name[0] == '-' || name[0] == '=' {
   176  		return &ErrFlagBadSyntax
   177  	}
   178  
   179  	// it's a flag. does it have an argument?
   180  	f.args = f.args[1:]
   181  	var value string
   182  	for i := 1; i < len(name); i++ { // equals cannot be first
   183  		if name[i] == '=' {
   184  			value = name[i+1:]
   185  			name = name[0:i]
   186  			break
   187  		}
   188  	}
   189  
   190  	err = f.Set(name, value)
   191  	return
   192  }