github.com/bhameyie/otto@v0.2.1-0.20160406174117-16052efa52ec/helper/flag/flag.go (about)

     1  package flag
     2  
     3  import (
     4  	"flag"
     5  )
     6  
     7  // FilterArgs filters the args slice to only include the the flags
     8  // in the given flagset and returns a new arg slice that has the
     9  // included args as well as a slice that has only the excluded args.
    10  // The final returned slice is the positional arguments.
    11  func FilterArgs(fs *flag.FlagSet, args []string) ([]string, []string, []string) {
    12  	// Optimistically make all the length of the arguments. There
    13  	// should never be so many arguments that this is too ineffecient.
    14  	inc := make([]string, 0, len(args))
    15  	exc := make([]string, 0, len(args))
    16  	pos := make([]string, 0, len(args))
    17  
    18  	// Make a map of the valid flags
    19  	flags := make(map[string]struct{})
    20  	fs.VisitAll(func(f *flag.Flag) {
    21  		flags[f.Name] = struct{}{}
    22  	})
    23  
    24  	// Go through each, parse out a single argument, and determine where
    25  	// it should go plus how many of the slots.
    26  	i := 0
    27  	for i < len(args) {
    28  		n, loc := filterOne(flags, args, i)
    29  
    30  		// Determine what slice to add the values to
    31  		var result *[]string
    32  		switch loc {
    33  		case filterLocBoth:
    34  			result = &pos
    35  		case filterLocInc:
    36  			result = &inc
    37  		case filterLocExc:
    38  			result = &exc
    39  		}
    40  
    41  		// Copy the values
    42  		*result = append(*result, args[i:i+n]...)
    43  
    44  		// Increment i so we continue moving through the arguments
    45  		i += n
    46  	}
    47  
    48  	return inc, exc, pos
    49  }
    50  
    51  type filterLoc byte
    52  
    53  const (
    54  	filterLocBoth filterLoc = iota
    55  	filterLocInc
    56  	filterLocExc
    57  )
    58  
    59  // filterOne is based very heavily on the official flag package
    60  // "parseOne" function. We do this on purpose so that we parse things
    61  // as similarly as possible in order to split the args.
    62  func filterOne(flags map[string]struct{}, args []string, i int) (int, filterLoc) {
    63  	// Get the arg
    64  	s := args[i]
    65  	if s == "-h" || s == "--help" {
    66  		return 1, filterLocInc
    67  	}
    68  
    69  	// If the arg is empty, not a flag, or just a "-" then we have to
    70  	// add it to BOTH lists.
    71  	if len(s) == 0 || s[0] != '-' || len(s) == 1 {
    72  		return 1, filterLocBoth
    73  	}
    74  
    75  	// If we hit double minuses, then we return the rest of the args to
    76  	// BOTH lists.
    77  	num_minuses := 1
    78  	if s[1] == '-' {
    79  		num_minuses++
    80  		if len(s) == 2 { // "--" terminates the flags
    81  			return len(args) - i, filterLocBoth
    82  		}
    83  	}
    84  
    85  	// Otherwise, get the name. If the syntax is invalid, let's just add it
    86  	// to both.
    87  	name := s[num_minuses:]
    88  	if len(name) == 0 || name[0] == '-' || name[0] == '=' {
    89  		return 1, filterLocBoth
    90  	}
    91  
    92  	// Check for an argument to the flag
    93  	has_value := false
    94  	for i := 1; i < len(name); i++ { // equals cannot be first
    95  		if name[i] == '=' {
    96  			has_value = true
    97  			name = name[0:i]
    98  			break
    99  		}
   100  	}
   101  
   102  	// Determine where this will go from here on out
   103  	pos := filterLocInc
   104  	if _, valid := flags[name]; !valid {
   105  		pos = filterLocExc
   106  	}
   107  
   108  	// It must have a value, which might be the next argument.
   109  	if !has_value && len(args) > i+1 {
   110  		return 2, pos
   111  	}
   112  
   113  	return 1, pos
   114  }