func Example() { type Inventory struct { Item string positional.Optional Count int } porch := Inventory{} err := positional.Parse(&porch, []string{"cat", "3"}) if err != nil { fmt.Println("error:", err) return } fmt.Printf("You have %d %s(s) on the porch\n", porch.Count, porch.Item) house := Inventory{ Count: 1, } err = positional.Parse(&house, []string{"dog"}) if err != nil { fmt.Println("error:", err) return } fmt.Printf("You have %d %s(s) in the house\n", house.Count, house.Item) // Output: // You have 3 cat(s) on the porch // You have 1 dog(s) in the house }
func TestParseEmpty(t *testing.T) { var args struct { } err := positional.Parse(&args, []string{}) if err != nil { t.Fatalf("unexpected error: %v", err) } }
func TestParseMandatory(t *testing.T) { var args struct { Foo string } err := positional.Parse(&args, []string{"one"}) if err != nil { t.Fatalf("unexpected error: %v", err) } if g, e := args.Foo, "one"; g != e { t.Errorf("unexpected value for Foo: %q != %q", g, e) } }
func TestParseInt(t *testing.T) { var args struct { Foo int } err := positional.Parse(&args, []string{"1"}) if err != nil { t.Fatalf("unexpected error: %v", err) } if g, e := args.Foo, 1; g != e { t.Errorf("unexpected value for Foo: %d != %d", g, e) } }
func TestParseOptionalMissing(t *testing.T) { var args struct { positional.Optional Foo string } err := positional.Parse(&args, []string{}) if err != nil { t.Fatalf("unexpected error: %v", err) } if g, e := args.Foo, ""; g != e { t.Errorf("unexpected value for Foo: %q != %q", g, e) } }
func TestParseBothMissingOptional(t *testing.T) { var args struct { Foo string positional.Optional Bar string } err := positional.Parse(&args, []string{"one"}) if err != nil { t.Fatalf("unexpected error: %v", err) } if g, e := args.Foo, "one"; g != e { t.Errorf("unexpected value for Foo: %q != %q", g, e) } if g, e := args.Bar, ""; g != e { t.Errorf("unexpected value for Bar: %q != %q", g, e) } }
func TestParseMandatoryMissing(t *testing.T) { var args struct { Foo string } err := positional.Parse(&args, []string{}) if err == nil { t.Fatalf("expected an error") } if _, ok := err.(positional.ErrMissingMandatoryArg); !ok { t.Errorf("unexpected error type: %T", err) } if err.Error() != "missing mandatory argument: FOO" { t.Errorf("unexpected error message: %q", err.Error()) } if g, e := args.Foo, ""; g != e { t.Errorf("unexpected value for Foo: %q != %q", g, e) } }
func TestParseTooMany(t *testing.T) { var args struct { Foo string } err := positional.Parse(&args, []string{"one", "two"}) if err == nil { t.Fatalf("expected an error") } if _, ok := err.(positional.ErrTooManyArgs); !ok { t.Errorf("unexpected error type: %T", err) } if err.Error() != "too many arguments" { t.Errorf("unexpected error message: %q", err.Error()) } if g, e := args.Foo, "one"; g != e { t.Errorf("unexpected value for Foo: %q != %q", g, e) } }
func TestParseMandatoryPlural(t *testing.T) { var args struct { Foo []string } err := positional.Parse(&args, []string{"one", "two"}) if err != nil { t.Fatalf("unexpected error: %v", err) } if g, e := len(args.Foo), 2; g != e { t.Errorf("unexpected length for Foo: %d != %d", g, e) } if g, e := args.Foo[0], "one"; g != e { t.Errorf("unexpected value for Foo[0]: %q != %q", g, e) } if g, e := args.Foo[1], "two"; g != e { t.Errorf("unexpected value for Foo[1]: %q != %q", g, e) } }
// Parse examines the command line from args based on the top-level // command cmd with the given name. It returns a Result that describes // the result of the parsing, and an error. Result is valid even when // error is not nil. func (s *Shell) Parse(cmd interface{}, name string, args []string) (Result, error) { result := Result{ parser: s, } pkg := pkgName(cmd) result.add(name, pkg, cmd) if pkg == "" { return result, fmt.Errorf("dispatch called for unnamed type: %v", cmd) } dispatch: for { // parse flags parser, ok := cmd.(FlagParser) if !ok { parser = &flag.FlagSet{} } if fl, ok := parser.(FlagSetter); ok { // we provide our own usage text, so this FlagSet name is only // used in programmer error related messages fl.Init(pkg, flag.ContinueOnError) // flag has a bad habit of polluting stderr in .Parse() *and* // getting the formatting wrong (e.g. we want to include // command name as prefix), plus we want to control error & // usage output to unify all the possible error sources; // silence Parse and output the error and usage later, in the // caller fl.SetOutput(ioutil.Discard) } err := parser.Parse(args) if err != nil { return result, err } args = parser.Args() // see if we have positional args to parse if cmd != nil { argsField := reflect.ValueOf(cmd).Elem().FieldByName("Arguments") if argsField.IsValid() { argsI := argsField.Addr().Interface() err := positional.Parse(argsI, args) if err != nil { return result, err } } } var hasSub bool for _, subcmd := range s.listSubcommands(pkg) { if len(args) > 0 && subcmd.pkg == pkg+"/"+args[0] { // exact match pkg = subcmd.pkg cmd = subcmd.cmd result.add(args[0], pkg, cmd) args = args[1:] continue dispatch } if len(args) > 0 && strings.HasPrefix(subcmd.pkg, pkg+"/"+args[0]+"/") { // step in the right direction pkg = pkg + "/" + args[0] cmd = nil result.add(args[0], pkg, cmd) args = args[1:] continue dispatch } if strings.HasPrefix(subcmd.pkg, pkg+"/") { _, runnable := cmd.(Runner) if len(args) == 0 && !runnable { // we have subcommands but no args return result, ErrMissingCommand{} } hasSub = true } } if len(args) > 0 && hasSub { return result, fmt.Errorf("command not found: %v", args[0]) } break } return result, nil }