diff options
-rw-r--r-- | err.go | 12 | ||||
-rw-r--r-- | opts.go | 56 | ||||
-rw-r--r-- | opts_test.go | 6 |
3 files changed, 60 insertions, 14 deletions
@@ -2,14 +2,18 @@ package opts import "fmt" -type ErrBadOption rune +// A BadOptionError describes an option that the user attempted to pass +// which the developer did not register. +type BadOptionError rune -func (e ErrBadOption) Error() string { +func (e BadOptionError) Error() string { return fmt.Sprintf("unknown option ‘%c’", e) } -type ErrNoArgument rune +// A NoArgumentError describes an option that the user attempted to pass +// without an argument, which required an argument. +type NoArgumentError rune -func (e ErrNoArgument) Error() string { +func (e NoArgumentError) Error() string { return fmt.Sprintf("expected argument for option ‘%c’", e) } @@ -1,24 +1,66 @@ +// Package opts implements unicode-aware getopt(3)- and getopt_long(3) +// flag parsing. +// +// The opts package aims to provide as simple an API as possible. If +// your usecase requires more advanced argument-parsing or a more robust +// API, this may not be the ideal package for you. +// +// While the opts package aims to closely follow the POSIX getopt(3) and +// GNU getopt_long(3) C functions, there are some notable differences. +// This package properly supports unicode flags, but also does not +// support a leading ‘:’ in the [Get] function’s option string; all +// user-facing I/O is delegrated to the caller. package opts +// ArgMode represents the whether or not a long-option takes an argument. type ArgMode int +// These tokens can be used to specify whether or not a long-option takes +// an argument. const ( - None ArgMode = iota - Required - Optional + None ArgMode = iota // long opt takes no argument + Required // long opt takes an argument + Optional // long opt optionally takes an argument ) +// Flag represents a parsed command-line flag. Key corresponds to the +// rune that was passed on the command-line, and Value corresponds to the +// flags argument if one was provided. In the case of long-options Key +// will map to the corresponding short-code, even if a long-option was +// used. type Flag struct { - Key rune - Value string + Key rune // the flag that was passed + Value string // the flags argument } +// LongOpt represents a long-option to attempt to parse. All long +// options have a short-hand form represented by Short and a long-form +// represented by Long. Arg is used to represent whether or not a takes +// an argument. +// +// In the case that you want to parse a long-option which doesn’t have a +// short-hand form, you can set Short to a negative integer. type LongOpt struct { Short rune Long string Arg ArgMode } +// Get parses the command-line arguments in args according to optstr. +// Unlike POSIX-getopt(3), a leading ‘:’ in optstr is not supported and +// will be ignored and no I/O is ever performed. +// +// Get will look for the flags listed in optstr (i.e., it will look for +// ‘-a’, ‘-ß’, and ‘λ’ given optstr == "aßλ"). The optstr need not be +// sorted in any particular order. If an option takes a required +// argument, it can be suffixed by a colon. If an option takes an +// optional argument, it can be suffixed by two colons. As an example, +// optstr == "a::ßλ:" will search for ‘-a’ with an optional argument, +// ‘-ß’ with no argument, and ‘-λ’ with a required argument. +// +// A successful parse returns the flags in the flags slice and the index +// of the first non-option argument in optind. In the case of failure, +// err will be one of [BadOptionError] or [NoArgumentError]. func Get(args []string, optstr string) (flags []Flag, optind int, err error) { argmap := make(map[rune]bool) @@ -48,13 +90,13 @@ func Get(args []string, optstr string) (flags []Flag, optind int, err error) { switch { case !ok: - return nil, 0, ErrBadOption(r) + return nil, 0, BadOptionError(r) case argp && j < len(rs)-1: s = string(rs[j+1:]) case argp: i++ if i >= len(args) { - return nil, 0, ErrNoArgument(r) + return nil, 0, NoArgumentError(r) } s = args[i] default: diff --git a/opts_test.go b/opts_test.go index a972867..2ea9334 100644 --- a/opts_test.go +++ b/opts_test.go @@ -35,7 +35,7 @@ func TestNoFlag(t *testing.T) { func TestCNoArg(t *testing.T) { args := []string{"foo", "-c"} - assertGet(t, args, 0, 0, ErrNoArgument('c')) + assertGet(t, args, 0, 0, NoArgumentError('c')) } func TestCWithArg(t *testing.T) { @@ -200,12 +200,12 @@ func TestΛAsArgToC(t *testing.T) { func TestInvalidFlag(t *testing.T) { args := []string{"foo", "-X"} - assertGet(t, args, 0, 0, ErrBadOption('X')) + assertGet(t, args, 0, 0, BadOptionError('X')) } func TestInvalidFlagWithArg(t *testing.T) { args := []string{"foo", "-X", "bar"} - assertGet(t, args, 0, 0, ErrBadOption('X')) + assertGet(t, args, 0, 0, BadOptionError('X')) } func TestAAfterArg(t *testing.T) { |