diff options
| author | Shulhan <ms@kilabit.info> | 2018-05-24 05:20:58 +0700 |
|---|---|---|
| committer | Shulhan <ms@kilabit.info> | 2018-05-24 05:20:58 +0700 |
| commit | 4f4754c2fbd7912f20e87da9d42cc04e660aa101 (patch) | |
| tree | 1c1457fb962bf12e96c0d291a058caf596a93057 | |
| parent | 4ec9107568ca6b619810a31defa69c3a8208b4b9 (diff) | |
| download | beku-4f4754c2fbd7912f20e87da9d42cc04e660aa101.tar.xz | |
cmd/beku: refactoring parsing flags
| -rw-r--r-- | README.md | 3 | ||||
| -rw-r--r-- | cmd/beku/command.go | 218 | ||||
| -rw-r--r-- | cmd/beku/command_test.go | 186 | ||||
| -rw-r--r-- | cmd/beku/main.go | 26 | ||||
| -rw-r--r-- | cmd/beku/operation.go | 3 |
5 files changed, 368 insertions, 68 deletions
@@ -50,7 +50,8 @@ Remove package "github.com/shuLhan/beku" source in "$GOPATH/src", their installed binaries in "$GOPATH/bin", and their installed archives on "$GOPATH/pkg/{GOOS}_{GOARCH}". - $ beku -Rs github.com/shuLhan/beku --recursive + $ beku -R github.com/shuLhan/beku --recursive + $ beku -Rs github.com/shuLhan/beku Remove package "github.com/shuLhan/beku" source in "$GOPATH/src", their installed binaries in "$GOPATH/bin", their installed archives on diff --git a/cmd/beku/command.go b/cmd/beku/command.go index 2544c99..208ea62 100644 --- a/cmd/beku/command.go +++ b/cmd/beku/command.go @@ -1,7 +1,7 @@ package main import ( - "flag" + "errors" "fmt" "log" "os" @@ -9,8 +9,15 @@ import ( "github.com/shuLhan/beku" ) +var ( + errInvalidOptions = errors.New("error: invalid options") + errMultiOperations = errors.New("error: only at operation may be used at a time") + errNoOperation = errors.New("error: no operation specified") + errNoTarget = errors.New("error: no targets specified") +) + const ( - emptyString = "" + emptyValue = "" flagUsageHelp = "Show the short usage." flagUsageQuery = "Query the package database." @@ -21,15 +28,10 @@ const ( ) type command struct { - op operation - env *beku.Env - help bool - query bool - recursive bool - queryPkg []string - rmPkg string - syncPkg string - syncInto string + op operation + env *beku.Env + pkgs []string + syncInto string } func (cmd *command) usage() { @@ -39,78 +41,180 @@ operations: ` + flagUsageHelp + ` beku {-Q|--query} [pkg ...] ` + flagUsageQuery + ` - beku {-R|--remove} [pkg ...] [-s|--recursive] + beku {-R|--remove} <pkg> [-s|--recursive] ` + flagUsageRemove + ` - beku {-S|--sync} <pkg@version> [--into <directory>] + beku {-S|--sync} <pkg[@version]> [--into <directory>] ` + flagUsageSync + ` ` - fmt.Print(help) + fmt.Fprint(os.Stderr, help) +} + +func (cmd *command) parseRemoveFlags(arg string) (operation, error) { + if len(arg) == 0 { + return opNone, nil + } + + var op operation + + switch arg[0] { + case 's': + op = opRecursive + return op, nil + } - os.Exit(1) + return opNone, errInvalidOptions } -func (cmd *command) setFlags() { - flag.Usage = cmd.usage +func (cmd *command) parseShortFlags(arg string) (operation, error) { + if len(arg) == 0 { + return opNone, errInvalidOptions + } - flag.BoolVar(&cmd.help, "h", false, flagUsageHelp) - flag.BoolVar(&cmd.help, "help", false, flagUsageHelp) + var ( + op operation + err error + ) - flag.BoolVar(&cmd.query, "Q", false, flagUsageQuery) - flag.BoolVar(&cmd.query, "query", false, flagUsageQuery) + switch arg[0] { + case 's': + op = opRecursive + if len(arg) > 1 { + return opNone, errInvalidOptions + } + case 'h': + op = opHelp + if len(arg) > 1 { + return opNone, errInvalidOptions + } + case 'Q': + op = opQuery + if len(arg) > 1 { + return opNone, errInvalidOptions + } + case 'S': + op = opSync + if len(arg) > 1 { + return opNone, errInvalidOptions + } + case 'R': + op, err = cmd.parseRemoveFlags(arg[1:]) + if err != nil { + return opNone, err + } + op |= opRemove + default: + return opNone, errInvalidOptions + } - flag.StringVar(&cmd.rmPkg, "R", emptyString, flagUsageRemove) - flag.StringVar(&cmd.rmPkg, "remove", emptyString, flagUsageRemove) + cmd.op |= op - flag.BoolVar(&cmd.recursive, "s", false, flagUsageRecursive) - flag.BoolVar(&cmd.recursive, "recursive", false, flagUsageRecursive) + return op, nil +} - flag.StringVar(&cmd.syncPkg, "S", emptyString, flagUsageSync) - flag.StringVar(&cmd.syncPkg, "sync", emptyString, flagUsageSync) - flag.StringVar(&cmd.syncInto, "into", emptyString, flagUsageSyncInto) +func (cmd *command) parseLongFlags(arg string) (op operation, err error) { + if len(arg) == 0 { + return opNone, errInvalidOptions + } + switch arg { + case "help": + op = opHelp + case "into": + op = opSyncInto + case "query": + op = opQuery + case "recursive": + op = opRecursive + case "remove": + op = opRemove + case "sync": + op = opSync + default: + return opNone, errInvalidOptions + } - flag.Parse() + cmd.op |= op + + return } // -// checkFlags +// parseFlags for multiple operations, invalid options, or empty targets. // -// (0) "-h" or "--help" is always the primary flag. +// (0) "-h" or "--help" flag is a stopper. +// (1) Only one operation is allowed. +// (2) "-R" or "-S" must have target // -func (cmd *command) checkFlags() { - // (0) - if cmd.help { - cmd.usage() +func (cmd *command) parseFlags(args []string) (err error) { + if len(args) == 0 { + return errNoOperation } - args := flag.Args() + var ( + fl int + op operation + ) + for _, arg := range args { + fl = 0 + for y, r := range arg { + if fl == 1 { + if r == '-' { + fl++ + continue + } + op, err = cmd.parseShortFlags(arg[y:]) + if err != nil { + return + } + break + } + if fl == 2 { + op, err = cmd.parseLongFlags(arg[y:]) + if err != nil { + return + } + break + } + if y == 0 && r == '-' { + fl++ + continue + } - if cmd.recursive { - cmd.op |= opRecursive + if op == opSyncInto { + cmd.syncInto = arg + } else { + cmd.pkgs = append(cmd.pkgs, arg) + } + break + } + // (0) + if op == opHelp { + return + } } - - if cmd.query { - cmd.op |= opQuery - cmd.queryPkg = args - return + if cmd.op == opRecursive || cmd.op == opSyncInto { + return errInvalidOptions } - - if len(cmd.syncPkg) > 0 { - cmd.op = opSync - - if len(cmd.syncInto) > 0 { - cmd.op |= opSyncInto + if cmd.op&opSyncInto == opSyncInto { + if cmd.op&opSync != opSync { + return errInvalidOptions } } - if len(cmd.rmPkg) > 0 { - cmd.op |= opRemove + // (1) + op = cmd.op & (opQuery | opRemove | opSync) + if op == opQuery|opRemove || op == opQuery|opSync || op == opRemove|opSync { + return errMultiOperations } - // Invalid command parameters - if cmd.op == opNone || cmd.op == opRecursive || cmd.op == opSyncInto { - cmd.usage() + // (2) + if op == opSync || op == opRemove { + if len(cmd.pkgs) == 0 { + return errNoTarget + } } + + return nil } func (cmd *command) loadDatabase() (err error) { @@ -141,8 +245,10 @@ func (cmd *command) firstTime() { func newCommand() (err error) { cmd = &command{} - cmd.setFlags() - cmd.checkFlags() + err = cmd.parseFlags(os.Args[1:]) + if err != nil { + return + } cmd.env, err = beku.NewEnvironment() if err != nil { diff --git a/cmd/beku/command_test.go b/cmd/beku/command_test.go new file mode 100644 index 0000000..48c8631 --- /dev/null +++ b/cmd/beku/command_test.go @@ -0,0 +1,186 @@ +package main + +import ( + "testing" + + "github.com/shuLhan/share/lib/test" +) + +func TestParseFlags(t *testing.T) { + cases := []struct { + args []string + expErr string + expCmd *command + }{{ + expErr: errNoOperation.Error(), + }, { + args: []string{"-s", "-"}, + expErr: errInvalidOptions.Error(), + }, { + args: []string{"-h"}, + expCmd: &command{ + op: opHelp, + }, + }, { + args: []string{"--help"}, + expCmd: &command{ + op: opHelp, + }, + }, { + args: []string{"--into"}, + expErr: errInvalidOptions.Error(), + }, { + args: []string{"-s"}, + expErr: errInvalidOptions.Error(), + }, { + args: []string{"--recursive"}, + expErr: errInvalidOptions.Error(), + }, { + args: []string{"--into", "directory"}, + expErr: errInvalidOptions.Error(), + }, { + args: []string{"-Q", "package", "--into", "directory"}, + expErr: errInvalidOptions.Error(), + }, { + args: []string{"-R", "package", "--into", "directory"}, + expErr: errInvalidOptions.Error(), + }, { + args: []string{"-Q", "query", "-R", "remove"}, + expErr: errMultiOperations.Error(), + }, { + args: []string{"-Q", "-R", "remove"}, + expErr: errMultiOperations.Error(), + }, { + args: []string{"-Q", "query", "-S", "sync"}, + expErr: errMultiOperations.Error(), + }, { + args: []string{"-S", "sync", "-R", "remove"}, + expErr: errMultiOperations.Error(), + }, { + args: []string{"-Q"}, + expCmd: &command{ + op: opQuery, + }, + }, { + args: []string{"--query"}, + expCmd: &command{ + op: opQuery, + }, + }, { + args: []string{"-Q", "-h"}, + expCmd: &command{ + op: opQuery | opHelp, + }, + }, { + args: []string{"A", "-Q", "B"}, + expCmd: &command{ + op: opQuery, + pkgs: []string{"A", "B"}, + }, + }, { + args: []string{"-S"}, + expErr: errNoTarget.Error(), + }, { + args: []string{"--sync"}, + expErr: errNoTarget.Error(), + }, { + args: []string{"-S", "package", "another"}, + expCmd: &command{ + op: opSync, + pkgs: []string{"package", "another"}, + }, + }, { + args: []string{"-S", "package", "--into", "directory"}, + expCmd: &command{ + op: opSync | opSyncInto, + pkgs: []string{"package"}, + syncInto: "directory", + }, + }, { + args: []string{"--sync", "A"}, + expCmd: &command{ + op: opSync, + pkgs: []string{"A"}, + }, + }, { + args: []string{"-R"}, + expErr: errNoTarget.Error(), + }, { + args: []string{"--remove"}, + expErr: errNoTarget.Error(), + }, { + args: []string{"-Rs"}, + expErr: errNoTarget.Error(), + }, { + args: []string{"-R", "A"}, + expCmd: &command{ + op: opRemove, + pkgs: []string{"A"}, + }, + }, { + args: []string{"--remove", "A"}, + expCmd: &command{ + op: opRemove, + pkgs: []string{"A"}, + }, + }, { + args: []string{"--remove", "-s", "A"}, + expCmd: &command{ + op: opRemove | opRecursive, + pkgs: []string{"A"}, + }, + }, { + args: []string{"--remove", "--recursive", "A"}, + expCmd: &command{ + op: opRemove | opRecursive, + pkgs: []string{"A"}, + }, + }, { + args: []string{"--remove", "A", "--recursive"}, + expCmd: &command{ + op: opRemove | opRecursive, + pkgs: []string{"A"}, + }, + }, { + args: []string{"--remove", "A", "---recursive"}, + expErr: errInvalidOptions.Error(), + }, { + args: []string{"-R", "A", "-s"}, + expCmd: &command{ + op: opRemove | opRecursive, + pkgs: []string{"A"}, + }, + }, { + args: []string{"-R", "A", "--recursive"}, + expCmd: &command{ + op: opRemove | opRecursive, + pkgs: []string{"A"}, + }, + }, { + args: []string{"-Rs", "A"}, + expCmd: &command{ + op: opRemove | opRecursive, + pkgs: []string{"A"}, + }, + }, { + args: []string{"-Rx", "A"}, + expErr: errInvalidOptions.Error(), + }, { + args: []string{"-T", "A"}, + expErr: errInvalidOptions.Error(), + }} + + for _, c := range cases { + t.Log(c.args) + + cmd := new(command) + + err := cmd.parseFlags(c.args) + if err != nil { + test.Assert(t, "err", c.expErr, err.Error(), true) + continue + } + + test.Assert(t, "cmd", c.expCmd, cmd, true) + } +} diff --git a/cmd/beku/main.go b/cmd/beku/main.go index 2425b7b..d869c21 100644 --- a/cmd/beku/main.go +++ b/cmd/beku/main.go @@ -42,7 +42,8 @@ // their installed binaries in "$GOPATH/bin", and their installed archives on // "$GOPATH/pkg/{GOOS}_{GOARCH}". // -// $ beku -Rs github.com/shuLhan/beku --recursive +// $ beku -R github.com/shuLhan/beku --recursive +// $ beku -Rs github.com/shuLhan/beku // // Remove package "github.com/shuLhan/beku" source in "$GOPATH/src", // their installed binaries in "$GOPATH/bin", their installed archives on @@ -109,7 +110,6 @@ package main import ( "fmt" - "log" "os" ) @@ -120,31 +120,37 @@ var ( func main() { err := newCommand() if err != nil { - log.Fatal(err) + fmt.Fprintln(os.Stderr, err) + os.Exit(1) } switch cmd.op { + case opHelp: + cmd.usage() + os.Exit(1) case opQuery: - cmd.env.Query(cmd.queryPkg) + cmd.env.Query(cmd.pkgs) case opRemove: - err = cmd.env.Remove(cmd.rmPkg, false) + err = cmd.env.Remove(cmd.pkgs[0], false) case opRemove | opRecursive: - err = cmd.env.Remove(cmd.rmPkg, true) + err = cmd.env.Remove(cmd.pkgs[0], true) case opSync: - err = cmd.env.Sync(cmd.syncPkg, "") + err = cmd.env.Sync(cmd.pkgs[0], "") case opSync | opSyncInto: - err = cmd.env.Sync(cmd.syncPkg, cmd.syncInto) + err = cmd.env.Sync(cmd.pkgs[0], cmd.syncInto) default: fmt.Fprintln(os.Stderr, "Invalid operations or options") os.Exit(1) } if err != nil { - log.Fatal(err) + fmt.Fprintln(os.Stderr, err) + os.Exit(1) } err = cmd.env.Save("") if err != nil { - log.Fatal(err) + fmt.Fprintln(os.Stderr, err) + os.Exit(1) } } diff --git a/cmd/beku/operation.go b/cmd/beku/operation.go index 86e0bf2..8b4a985 100644 --- a/cmd/beku/operation.go +++ b/cmd/beku/operation.go @@ -5,7 +5,8 @@ type operation uint const opNone operation = 0 const ( - opQuery operation = 1 << iota + opHelp operation = 1 << iota + opQuery opRecursive opRemove opSync |
