package cmd
import (
+ "flag"
"fmt"
"io"
)
}
}
}
+
+// WithLateSubcommand wraps a RunFunc by skipping over some known
+// flags to find a subcommand, and moving that subcommand to the front
+// of the args before calling the wrapped RunFunc. For example:
+//
+// // Translate [ --format foo subcommand bar]
+// // to [subcommand --format foo bar]
+// WithLateSubcommand(fn, []string{"format"}, nil)
+func WithLateSubcommand(run RunFunc, argFlags, boolFlags []string) RunFunc {
+ return func(prog string, args []string, stdin io.Reader, stdout, stderr io.Writer) int {
+ flags := flag.NewFlagSet("prog", flag.ContinueOnError)
+ for _, arg := range argFlags {
+ flags.String(arg, "", "")
+ }
+ for _, arg := range boolFlags {
+ flags.Bool(arg, false, "")
+ }
+ // Ignore errors. We can't report a useful error
+ // message anyway.
+ flags.Parse(args)
+ if flags.NArg() > 0 {
+ // Move the first arg after the recognized
+ // flags up to the front.
+ flagargs := len(args) - flags.NArg()
+ newargs := make([]string, len(args))
+ newargs[0] = args[flagargs]
+ copy(newargs[1:flagargs+1], args[:flagargs])
+ copy(newargs[flagargs+1:], args[flagargs+1:])
+ args = newargs
+ }
+ return run(prog, args, stdin, stdout, stderr)
+ }
+}
--- /dev/null
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: Apache-2.0
+
+package cmd
+
+import (
+ "bytes"
+ "fmt"
+ "io"
+ "strings"
+ "testing"
+
+ check "gopkg.in/check.v1"
+)
+
+// Gocheck boilerplate
+func Test(t *testing.T) {
+ check.TestingT(t)
+}
+
+var _ = check.Suite(&CmdSuite{})
+
+type CmdSuite struct{}
+
+var testCmd = Multi(map[string]RunFunc{
+ "echo": func(prog string, args []string, stdin io.Reader, stdout io.Writer, stderr io.Writer) int {
+ fmt.Fprintln(stdout, strings.Join(args, " "))
+ return 0
+ },
+})
+
+func (s *CmdSuite) TestHello(c *check.C) {
+ stdout := bytes.NewBuffer(nil)
+ stderr := bytes.NewBuffer(nil)
+ exited := testCmd("prog", []string{"echo", "hello", "world"}, bytes.NewReader(nil), stdout, stderr)
+ c.Check(exited, check.Equals, 0)
+ c.Check(stdout.String(), check.Equals, "hello world\n")
+ c.Check(stderr.String(), check.Equals, "")
+}
+
+func (s *CmdSuite) TestWithLateSubcommand(c *check.C) {
+ stdout := bytes.NewBuffer(nil)
+ stderr := bytes.NewBuffer(nil)
+ run := WithLateSubcommand(testCmd, []string{"format", "f"}, []string{"n"})
+ exited := run("prog", []string{"--format=yaml", "-n", "-format", "beep", "echo", "hi"}, bytes.NewReader(nil), stdout, stderr)
+ c.Check(exited, check.Equals, 0)
+ c.Check(stdout.String(), check.Equals, "--format=yaml -n -format beep hi\n")
+ c.Check(stderr.String(), check.Equals, "")
+}