func (a *Args) Duration(name, help string, opts ...ArgOption) {
	a.register(name, help, "duration", false,
		func(args []string, res ArgMap) ([]string, error) {
			d, err := time.ParseDuration(args[0])
			if err != nil {
				return nil, fmt.Errorf("invalid duration value '%s' for argument: %s", args[0], name)
			}
			res[name] = &ArgMapItem{Value: d}
			return args[1:], nil
		},
		opts...,
	)
}
Duration registers a duration argument.
package grumble
import (
	"fmt"
	"strconv"
	"time"
)
type parseArgFunc func(args []string, res ArgMap) ([]string, error)
type argItem struct {
	Name     string
	Help     string
	HelpArgs string
	Default  interface{}
	parser   parseArgFunc
	isList   bool
	optional bool
	listMin  int
	listMax  int
}
type Args struct {
	list []*argItem
}
func (a *Args) register(
	name, help, helpArgs string,
	isList bool,
	pf parseArgFunc,
	opts ...ArgOption,
) {
	if name == "" {
		panic("empty argument name")
	} else if help == "" {
		panic(fmt.Errorf("missing help message for argument '%s'", name))
	}
	for _, ai := range a.list {
		if ai.Name == name {
			panic(fmt.Errorf("argument '%s' registered twice", name))
		}
	}
	item := &argItem{
		Name:     name,
		Help:     help,
		HelpArgs: helpArgs,
		parser:   pf,
		isList:   isList,
		optional: isList,
		listMin:  -1,
		listMax:  -1,
	}
	for _, opt := range opts {
		opt(item)
	}
	if item.isList && item.listMax > 0 && item.listMax < item.listMin {
		panic("max must not be less than min for list arguments")
	}
	if !a.empty() {
		last := a.list[len(a.list)-1]
		if last.isList {
			panic("list argument has been registered, nothing can come after it")
		}
		if !item.optional && last.optional {
			panic("mandatory argument not allowed after optional one")
		}
	}
	a.list = append(a.list, item)
}
func (a *Args) empty() bool {
	return len(a.list) == 0
}
func (a *Args) parse(args []string, res ArgMap) ([]string, error) {
	var err error
	for _, item := range a.list {
		if item.isList {
			if len(args) < item.listMin {
				return nil, fmt.Errorf("argument '%s' requires at least %d element(s)", item.Name, item.listMin)
			}
			if item.listMax > 0 && len(args) > item.listMax {
				return nil, fmt.Errorf("argument '%s' requires at most %d element(s)", item.Name, item.listMax)
			}
		}
		if len(args) == 0 {
			if !item.optional {
				return nil, fmt.Errorf("missing argument '%s'", item.Name)
			}
			res[item.Name] = &ArgMapItem{Value: item.Default, IsDefault: true}
			continue
		}
		args, err = item.parser(args, res)
		if err != nil {
			return nil, err
		}
	}
	return args, nil
}
func (a *Args) String(name, help string, opts ...ArgOption) {
	a.register(name, help, "string", false,
		func(args []string, res ArgMap) ([]string, error) {
			res[name] = &ArgMapItem{Value: args[0]}
			return args[1:], nil
		},
		opts...,
	)
}
func (a *Args) StringList(name, help string, opts ...ArgOption) {
	a.register(name, help, "string list", true,
		func(args []string, res ArgMap) ([]string, error) {
			res[name] = &ArgMapItem{Value: args}
			return []string{}, nil
		},
		opts...,
	)
}
func (a *Args) Bool(name, help string, opts ...ArgOption) {
	a.register(name, help, "bool", false,
		func(args []string, res ArgMap) ([]string, error) {
			b, err := strconv.ParseBool(args[0])
			if err != nil {
				return nil, fmt.Errorf("invalid bool value '%s' for argument: %s", args[0], name)
			}
			res[name] = &ArgMapItem{Value: b}
			return args[1:], nil
		},
		opts...,
	)
}
func (a *Args) BoolList(name, help string, opts ...ArgOption) {
	a.register(name, help, "bool list", true,
		func(args []string, res ArgMap) ([]string, error) {
			var (
				err error
				bs  = make([]bool, len(args))
			)
			for i, a := range args {
				bs[i], err = strconv.ParseBool(a)
				if err != nil {
					return nil, fmt.Errorf("invalid bool value '%s' for argument: %s", a, name)
				}
			}
			res[name] = &ArgMapItem{Value: bs}
			return []string{}, nil
		},
		opts...,
	)
}
func (a *Args) Int(name, help string, opts ...ArgOption) {
	a.register(name, help, "int", false,
		func(args []string, res ArgMap) ([]string, error) {
			i, err := strconv.Atoi(args[0])
			if err != nil {
				return nil, fmt.Errorf("invalid int value '%s' for argument: %s", args[0], name)
			}
			res[name] = &ArgMapItem{Value: i}
			return args[1:], nil
		},
		opts...,
	)
}
func (a *Args) IntList(name, help string, opts ...ArgOption) {
	a.register(name, help, "int list", true,
		func(args []string, res ArgMap) ([]string, error) {
			var (
				err error
				is  = make([]int, len(args))
			)
			for i, a := range args {
				is[i], err = strconv.Atoi(a)
				if err != nil {
					return nil, fmt.Errorf("invalid int value '%s' for argument: %s", a, name)
				}
			}
			res[name] = &ArgMapItem{Value: is}
			return []string{}, nil
		},
		opts...,
	)
}
func (a *Args) Int64(name, help string, opts ...ArgOption) {
	a.register(name, help, "int64", false,
		func(args []string, res ArgMap) ([]string, error) {
			i, err := strconv.ParseInt(args[0], 10, 64)
			if err != nil {
				return nil, fmt.Errorf("invalid int64 value '%s' for argument: %s", args[0], name)
			}
			res[name] = &ArgMapItem{Value: i}
			return args[1:], nil
		},
		opts...,
	)
}
func (a *Args) Int64List(name, help string, opts ...ArgOption) {
	a.register(name, help, "int64 list", true,
		func(args []string, res ArgMap) ([]string, error) {
			var (
				err error
				is  = make([]int64, len(args))
			)
			for i, a := range args {
				is[i], err = strconv.ParseInt(a, 10, 64)
				if err != nil {
					return nil, fmt.Errorf("invalid int64 value '%s' for argument: %s", a, name)
				}
			}
			res[name] = &ArgMapItem{Value: is}
			return []string{}, nil
		},
		opts...,
	)
}
func (a *Args) Uint(name, help string, opts ...ArgOption) {
	a.register(name, help, "uint", false,
		func(args []string, res ArgMap) ([]string, error) {
			u, err := strconv.ParseUint(args[0], 10, 64)
			if err != nil {
				return nil, fmt.Errorf("invalid uint value '%s' for argument: %s", args[0], name)
			}
			res[name] = &ArgMapItem{Value: uint(u)}
			return args[1:], nil
		},
		opts...,
	)
}
func (a *Args) UintList(name, help string, opts ...ArgOption) {
	a.register(name, help, "uint list", true,
		func(args []string, res ArgMap) ([]string, error) {
			var (
				err error
				u   uint64
				is  = make([]uint, len(args))
			)
			for i, a := range args {
				u, err = strconv.ParseUint(a, 10, 64)
				if err != nil {
					return nil, fmt.Errorf("invalid uint value '%s' for argument: %s", a, name)
				}
				is[i] = uint(u)
			}
			res[name] = &ArgMapItem{Value: is}
			return []string{}, nil
		},
		opts...,
	)
}
func (a *Args) Uint64(name, help string, opts ...ArgOption) {
	a.register(name, help, "uint64", false,
		func(args []string, res ArgMap) ([]string, error) {
			u, err := strconv.ParseUint(args[0], 10, 64)
			if err != nil {
				return nil, fmt.Errorf("invalid uint64 value '%s' for argument: %s", args[0], name)
			}
			res[name] = &ArgMapItem{Value: u}
			return args[1:], nil
		},
		opts...,
	)
}
func (a *Args) Uint64List(name, help string, opts ...ArgOption) {
	a.register(name, help, "uint64 list", true,
		func(args []string, res ArgMap) ([]string, error) {
			var (
				err error
				us  = make([]uint64, len(args))
			)
			for i, a := range args {
				us[i], err = strconv.ParseUint(a, 10, 64)
				if err != nil {
					return nil, fmt.Errorf("invalid uint64 value '%s' for argument: %s", a, name)
				}
			}
			res[name] = &ArgMapItem{Value: us}
			return []string{}, nil
		},
		opts...,
	)
}
func (a *Args) Float64(name, help string, opts ...ArgOption) {
	a.register(name, help, "float64", false,
		func(args []string, res ArgMap) ([]string, error) {
			f, err := strconv.ParseFloat(args[0], 64)
			if err != nil {
				return nil, fmt.Errorf("invalid float64 value '%s' for argument: %s", args[0], name)
			}
			res[name] = &ArgMapItem{Value: f}
			return args[1:], nil
		},
		opts...,
	)
}
func (a *Args) Float64List(name, help string, opts ...ArgOption) {
	a.register(name, help, "float64 list", true,
		func(args []string, res ArgMap) ([]string, error) {
			var (
				err error
				fs  = make([]float64, len(args))
			)
			for i, a := range args {
				fs[i], err = strconv.ParseFloat(a, 64)
				if err != nil {
					return nil, fmt.Errorf("invalid float64 value '%s' for argument: %s", a, name)
				}
			}
			res[name] = &ArgMapItem{Value: fs}
			return []string{}, nil
		},
		opts...,
	)
}