func process(specs []*spec, args []string) error {
	// track the options we have seen
	wasPresent := make(map[*spec]bool)

	// construct a map from --option to spec
	optionMap := make(map[string]*spec)
	for _, spec := range specs {
		if spec.positional {
			continue
		}
		if spec.long != "" {
			optionMap[spec.long] = spec
		}
		if spec.short != "" {
			optionMap[spec.short] = spec
		}
	}

	// deal with environment vars
	for _, spec := range specs {
		if spec.env == "" {
			continue
		}

		value, found := os.LookupEnv(spec.env)
		if !found {
			continue
		}

		if spec.multiple {
			// expect a CSV string in an environment
			// variable in the case of multiple values
			values, err := csv.NewReader(strings.NewReader(value)).Read()
			if err != nil {
				return fmt.Errorf(
					"error reading a CSV string from environment variable %s with multiple values: %v",
					spec.env,
					err,
				)
			}
			if err = setSlice(spec.dest, values, !spec.separate); err != nil {
				return fmt.Errorf(
					"error processing environment variable %s with multiple values: %v",
					spec.env,
					err,
				)
			}
		} else {
			if err := scalar.ParseValue(spec.dest, value); err != nil {
				return fmt.Errorf("error processing environment variable %s: %v", spec.env, err)
			}
		}
		wasPresent[spec] = true
	}

	// process each string from the command line
	var allpositional bool
	var positionals []string

	// must use explicit for loop, not range, because we manipulate i inside the loop
	for i := 0; i < len(args); i++ {
		arg := args[i]
		if arg == "--" {
			allpositional = true
			continue
		}

		if !isFlag(arg) || allpositional {
			positionals = append(positionals, arg)
			continue
		}

		// check for an equals sign, as in "--foo=bar"
		var value string
		opt := strings.TrimLeft(arg, "-")
		if pos := strings.Index(opt, "="); pos != -1 {
			value = opt[pos+1:]
			opt = opt[:pos]
		}

		// lookup the spec for this option
		spec, ok := optionMap[opt]
		if !ok {
			return fmt.Errorf("unknown argument %s", arg)
		}
		wasPresent[spec] = true

		// deal with the case of multiple values
		if spec.multiple {
			var values []string
			if value == "" {
				for i+1 < len(args) && !isFlag(args[i+1]) {
					values = append(values, args[i+1])
					i++
					if spec.separate {
						break
					}
				}
			} else {
				values = append(values, value)
			}
			err := setSlice(spec.dest, values, !spec.separate)
			if err != nil {
				return fmt.Errorf("error processing %s: %v", arg, err)
			}
			continue
		}

		// if it's a flag and it has no value then set the value to true
		// use boolean because this takes account of TextUnmarshaler
		if spec.boolean && value == "" {
			value = "true"
		}

		// if we have something like "--foo" then the value is the next argument
		if value == "" {
			if i+1 == len(args) {
				return fmt.Errorf("missing value for %s", arg)
			}
			if !nextIsNumeric(spec.dest.Type(), args[i+1]) && isFlag(args[i+1]) {
				return fmt.Errorf("missing value for %s", arg)
			}
			value = args[i+1]
			i++
		}

		err := scalar.ParseValue(spec.dest, value)
		if err != nil {
			return fmt.Errorf("error processing %s: %v", arg, err)
		}
	}

	// process positionals
	for _, spec := range specs {
		if !spec.positional {
			continue
		}
		if len(positionals) == 0 {
			break
		}
		wasPresent[spec] = true
		if spec.multiple {
			err := setSlice(spec.dest, positionals, true)
			if err != nil {
				return fmt.Errorf("error processing %s: %v", spec.long, err)
			}
			positionals = nil
		} else {
			err := scalar.ParseValue(spec.dest, positionals[0])
			if err != nil {
				return fmt.Errorf("error processing %s: %v", spec.long, err)
			}
			positionals = positionals[1:]
		}
	}
	if len(positionals) > 0 {
		return fmt.Errorf("too many positional arguments at '%s'", positionals[0])
	}

	// finally check that all the required args were provided
	for _, spec := range specs {
		if spec.required && !wasPresent[spec] {
			name := spec.long
			if !spec.positional {
				name = "--" + spec.long
			}
			return fmt.Errorf("%s is required", name)
		}
	}

	return nil
}
func process(specs []*spec, args []string) error {
	
	wasPresent := make(map[*spec]bool)

	
	optionMap := make(map[string]*spec)
	for _, spec := range specs {
		if spec.positional {
			continue
		}
		if spec.long != "" {
			optionMap[spec.long] = spec
		}
		if spec.short != "" {
			optionMap[spec.short] = spec
		}
	}

	
	for _, spec := range specs {
		if spec.env == "" {
			continue
		}

		value, found := os.LookupEnv(spec.env)
		if !found {
			continue
		}

		if spec.multiple {
			
			
			values, err := csv.NewReader(strings.NewReader(value)).Read()
			if err != nil {
				return fmt.Errorf(
					"error reading a CSV string from environment variable %s with multiple values: %v",
					spec.env,
					err,
				)
			}
			if err = setSlice(spec.dest, values, !spec.separate); err != nil {
				return fmt.Errorf(
					"error processing environment variable %s with multiple values: %v",
					spec.env,
					err,
				)
			}
		} else {
			if err := scalar.ParseValue(spec.dest, value); err != nil {
				return fmt.Errorf("error processing environment variable %s: %v", spec.env, err)
			}
		}
		wasPresent[spec] = true
	}

	
	var allpositional bool
	var positionals []string

	
	for i := 0; i < len(args); i++ {
		arg := args[i]
		if arg == "--" {
			allpositional = true
			continue
		}

		if !isFlag(arg) || allpositional {
			positionals = append(positionals, arg)
			continue
		}

		
		var value string
		opt := strings.TrimLeft(arg, "-")
		if pos := strings.Index(opt, "="); pos != -1 {
			value = opt[pos+1:]
			opt = opt[:pos]
		}

		
		spec, ok := optionMap[opt]
		if !ok {
			return fmt.Errorf("unknown argument %s", arg)
		}
		wasPresent[spec] = true

		
		if spec.multiple {
			var values []string
			if value == "" {
				for i+1 < len(args) && !isFlag(args[i+1]) {
					values = append(values, args[i+1])
					i++
					if spec.separate {
						break
					}
				}
			} else {
				values = append(values, value)
			}
			err := setSlice(spec.dest, values, !spec.separate)
			if err != nil {
				return fmt.Errorf("error processing %s: %v", arg, err)
			}
			continue
		}

		
		
		if spec.boolean && value == "" {
			value = "true"
		}

		
		if value == "" {
			if i+1 == len(args) {
				return fmt.Errorf("missing value for %s", arg)
			}
			if !nextIsNumeric(spec.dest.Type(), args[i+1]) && isFlag(args[i+1]) {
				return fmt.Errorf("missing value for %s", arg)
			}
			value = args[i+1]
			i++
		}

		err := scalar.ParseValue(spec.dest, value)
		if err != nil {
			return fmt.Errorf("error processing %s: %v", arg, err)
		}
	}

	
	for _, spec := range specs {
		if !spec.positional {
			continue
		}
		if len(positionals) == 0 {
			break
		}
		wasPresent[spec] = true
		if spec.multiple {
			err := setSlice(spec.dest, positionals, true)
			if err != nil {
				return fmt.Errorf("error processing %s: %v", spec.long, err)
			}
			positionals = nil
		} else {
			err := scalar.ParseValue(spec.dest, positionals[0])
			if err != nil {
				return fmt.Errorf("error processing %s: %v", spec.long, err)
			}
			positionals = positionals[1:]
		}
	}
	if len(positionals) > 0 {
		return fmt.Errorf("too many positional arguments at '%s'", positionals[0])
	}

	
	for _, spec := range specs {
		if spec.required && !wasPresent[spec] {
			name := spec.long
			if !spec.positional {
				name = "--" + spec.long
			}
			return fmt.Errorf("%s is required", name)
		}
	}

	return nil
}
func process ( specs [ ] * spec , args [ ] string ) error { // track the options we have seen wasPresent := make ( map [ * spec ] bool ) // construct a map from --option to spec optionMap := make ( map [ string ] * spec ) for _ , spec := range specs { if spec . positional { continue } if spec . long != " " { optionMap [ spec . long ] = spec } if spec . short != " " { optionMap [ spec . short ] = spec } } // deal with environment vars for _ , spec := range specs { if spec . env == " " { continue } value , found := os . LookupEnv ( spec . env ) if ! found { continue } if spec . multiple { // expect a CSV string in an environment // variable in the case of multiple values values , err := csv . NewReader ( strings . NewReader ( value ) ) . Read ( ) if err != nil { return fmt . Errorf ( " " , spec . env , err , ) } if err = setSlice ( spec . dest , values , ! spec . separate ) ; err != nil { return fmt . Errorf ( " " , spec . env , err , ) } } else { if err := scalar . ParseValue ( spec . dest , value ) ; err != nil { return fmt . Errorf ( " " , spec . env , err ) } } wasPresent [ spec ] = true } // process each string from the command line var allpositional bool var positionals [ ] string // must use explicit for loop, not range, because we manipulate i inside the loop for i := 0 ; i < len ( args ) ; i ++ { arg := args [ i ] if arg == " " { allpositional = true continue } if ! isFlag ( arg ) || allpositional { positionals = append ( positionals , arg ) continue } // check for an equals sign, as in "--foo=bar" var value string opt := strings . TrimLeft ( arg , " " ) if pos := strings . Index ( opt , " " ) ; pos != - 1 { value = opt [ pos + 1 : ] opt = opt [ : pos ] } // lookup the spec for this option spec , ok := optionMap [ opt ] if ! ok { return fmt . Errorf ( " " , arg ) } wasPresent [ spec ] = true // deal with the case of multiple values if spec . multiple { var values [ ] string if value == " " { for i + 1 < len ( args ) && ! isFlag ( args [ i + 1 ] ) { values = append ( values , args [ i + 1 ] ) i ++ if spec . separate { break } } } else { values = append ( values , value ) } err := setSlice ( spec . dest , values , ! spec . separate ) if err != nil { return fmt . Errorf ( " " , arg , err ) } continue } // if it's a flag and it has no value then set the value to true // use boolean because this takes account of TextUnmarshaler if spec . boolean && value == " " { value = " " } // if we have something like "--foo" then the value is the next argument if value == " " { if i + 1 == len ( args ) { return fmt . Errorf ( " " , arg ) } if ! nextIsNumeric ( spec . dest . Type ( ) , args [ i + 1 ] ) && isFlag ( args [ i + 1 ] ) { return fmt . Errorf ( " " , arg ) } value = args [ i + 1 ] i ++ } err := scalar . ParseValue ( spec . dest , value ) if err != nil { return fmt . Errorf ( " " , arg , err ) } } // process positionals for _ , spec := range specs { if ! spec . positional { continue } if len ( positionals ) == 0 { break } wasPresent [ spec ] = true if spec . multiple { err := setSlice ( spec . dest , positionals , true ) if err != nil { return fmt . Errorf ( " " , spec . long , err ) } positionals = nil } else { err := scalar . ParseValue ( spec . dest , positionals [ 0 ] ) if err != nil { return fmt . Errorf ( " " , spec . long , err ) } positionals = positionals [ 1 : ] } } if len ( positionals ) > 0 { return fmt . Errorf ( " " , positionals [ 0 ] ) } // finally check that all the required args were provided for _ , spec := range specs { if spec . required && ! wasPresent [ spec ] { name := spec . long if ! spec . positional { name = " " + spec . long } return fmt . Errorf ( " " , name ) } } return nil }
// process goes through arguments one-by-one, parses them, and assigns the result to
// the underlying struct field
process goes through arguments one - by - one parses them and assigns the result to the underlying struct field