aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorShulhan <ms@kilabit.info>2018-05-24 05:20:58 +0700
committerShulhan <ms@kilabit.info>2018-05-24 05:20:58 +0700
commit4f4754c2fbd7912f20e87da9d42cc04e660aa101 (patch)
tree1c1457fb962bf12e96c0d291a058caf596a93057
parent4ec9107568ca6b619810a31defa69c3a8208b4b9 (diff)
downloadbeku-4f4754c2fbd7912f20e87da9d42cc04e660aa101.tar.xz
cmd/beku: refactoring parsing flags
-rw-r--r--README.md3
-rw-r--r--cmd/beku/command.go218
-rw-r--r--cmd/beku/command_test.go186
-rw-r--r--cmd/beku/main.go26
-rw-r--r--cmd/beku/operation.go3
5 files changed, 368 insertions, 68 deletions
diff --git a/README.md b/README.md
index 78fbcd7..f73a8d8 100644
--- a/README.md
+++ b/README.md
@@ -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