Compare commits

...

3 commits

Author SHA1 Message Date
Sung
ff7e3a4cee wip 2023-03-12 13:46:28 +11:00
Sung
37b137cbe7 Add command package 2023-03-11 22:32:16 +11:00
Sung
e98403428e Remove autoclose of inactive issue 2023-03-11 18:07:58 +11:00
16 changed files with 354 additions and 113 deletions

View file

@ -1,49 +0,0 @@
name: CI
on:
push:
branches:
- master
pull_request:
branches:
- master
jobs:
build:
runs-on: ubuntu-22.04
services:
postgres:
image: postgres:14
env:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
POSTGRES_DB: dnote_test
POSTGRES_PORT: 5432
# Wait until postgres has started
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
# Expose port to the host
ports:
- 5432:5432
steps:
- uses: actions/checkout@v3
- uses: actions/setup-go@v3
with:
go-version: '1.20.0'
- name: Install dependencies
run: |
make install
- name: Test cli
run: |
make test-cli
- name: Test app
run: |
make test-api

View file

@ -20,9 +20,10 @@ package add
import (
"database/sql"
"time"
"os"
"time"
"github.com/dnote/dnote/pkg/cli/command"
"github.com/dnote/dnote/pkg/cli/context"
"github.com/dnote/dnote/pkg/cli/database"
"github.com/dnote/dnote/pkg/cli/infra"
@ -33,7 +34,6 @@ import (
"github.com/dnote/dnote/pkg/cli/utils"
"github.com/dnote/dnote/pkg/cli/validate"
"github.com/pkg/errors"
"github.com/spf13/cobra"
)
var contentFlag string
@ -52,7 +52,7 @@ var example = `
pull is fetch with a merge
EOF`
func preRun(cmd *cobra.Command, args []string) error {
func preRun(cmd *command.Command, args []string) error {
if len(args) != 1 {
return errors.New("Incorrect number of argument")
}
@ -61,8 +61,9 @@ func preRun(cmd *cobra.Command, args []string) error {
}
// NewCmd returns a new add command
func NewCmd(ctx context.DnoteCtx) *cobra.Command {
cmd := &cobra.Command{
func NewCmd(ctx context.DnoteCtx) *command.Command {
cmd := &command.Command{
Name: "add",
Use: "add <book>",
Short: "Add a new note",
Aliases: []string{"a", "n", "new"},
@ -84,7 +85,7 @@ func getContent(ctx context.DnoteCtx) (string, error) {
// check for piped content
fInfo, _ := os.Stdin.Stat()
if fInfo.Mode() & os.ModeCharDevice == 0 {
if fInfo.Mode()&os.ModeCharDevice == 0 {
c, err := ui.ReadStdInput()
if err != nil {
return "", errors.Wrap(err, "Failed to get piped input")
@ -106,7 +107,7 @@ func getContent(ctx context.DnoteCtx) (string, error) {
}
func newRun(ctx context.DnoteCtx) infra.RunEFunc {
return func(cmd *cobra.Command, args []string) error {
return func(cmd *command.Command, args []string) error {
bookName := args[0]
if err := validate.BookName(bookName); err != nil {
return errors.Wrap(err, "invalid book name")

View file

@ -21,13 +21,13 @@ package cat
import (
"strconv"
"github.com/dnote/dnote/pkg/cli/command"
"github.com/dnote/dnote/pkg/cli/context"
"github.com/dnote/dnote/pkg/cli/database"
"github.com/dnote/dnote/pkg/cli/infra"
"github.com/dnote/dnote/pkg/cli/log"
"github.com/dnote/dnote/pkg/cli/output"
"github.com/pkg/errors"
"github.com/spf13/cobra"
)
var example = `
@ -40,7 +40,7 @@ var deprecationWarning = `and "view" will replace it in the future version.
Run "dnote view --help" for more information.
`
func preRun(cmd *cobra.Command, args []string) error {
func preRun(cmd *command.Command, args []string) error {
if len(args) != 2 {
return errors.New("Incorrect number of arguments")
}
@ -49,8 +49,8 @@ func preRun(cmd *cobra.Command, args []string) error {
}
// NewCmd returns a new cat command
func NewCmd(ctx context.DnoteCtx) *cobra.Command {
cmd := &cobra.Command{
func NewCmd(ctx context.DnoteCtx) *command.Command {
cmd := &command.Command{
Use: "cat <book name> <note index>",
Aliases: []string{"c"},
Short: "See a note",
@ -65,7 +65,7 @@ func NewCmd(ctx context.DnoteCtx) *cobra.Command {
// NewRun returns a new run function
func NewRun(ctx context.DnoteCtx, contentOnly bool) infra.RunEFunc {
return func(cmd *cobra.Command, args []string) error {
return func(cmd *command.Command, args []string) error {
var noteRowIDArg string
if len(args) == 2 {

View file

@ -19,12 +19,12 @@
package edit
import (
"github.com/dnote/dnote/pkg/cli/command"
"github.com/dnote/dnote/pkg/cli/context"
"github.com/dnote/dnote/pkg/cli/infra"
"github.com/dnote/dnote/pkg/cli/log"
"github.com/dnote/dnote/pkg/cli/utils"
"github.com/pkg/errors"
"github.com/spf13/cobra"
)
var contentFlag string
@ -49,8 +49,8 @@ var example = `
`
// NewCmd returns a new edit command
func NewCmd(ctx context.DnoteCtx) *cobra.Command {
cmd := &cobra.Command{
func NewCmd(ctx context.DnoteCtx) *command.Command {
cmd := &command.Command{
Use: "edit <note id|book name>",
Short: "Edit a note or a book",
Aliases: []string{"e"},
@ -67,7 +67,7 @@ func NewCmd(ctx context.DnoteCtx) *cobra.Command {
return cmd
}
func preRun(cmd *cobra.Command, args []string) error {
func preRun(cmd *command.Command, args []string) error {
if len(args) != 1 && len(args) != 2 {
return errors.New("Incorrect number of argument")
}
@ -76,7 +76,7 @@ func preRun(cmd *cobra.Command, args []string) error {
}
func newRun(ctx context.DnoteCtx) infra.RunEFunc {
return func(cmd *cobra.Command, args []string) error {
return func(cmd *command.Command, args []string) error {
// DEPRECATED: Remove in 1.0.0
if len(args) == 2 {
log.Plain(log.ColorYellow.Sprintf("DEPRECATED: you no longer need to pass book name to the view command. e.g. `dnote view 123`.\n\n"))

View file

@ -23,11 +23,11 @@ import (
"fmt"
"strings"
"github.com/dnote/dnote/pkg/cli/command"
"github.com/dnote/dnote/pkg/cli/context"
"github.com/dnote/dnote/pkg/cli/infra"
"github.com/dnote/dnote/pkg/cli/log"
"github.com/pkg/errors"
"github.com/spf13/cobra"
)
var example = `
@ -43,7 +43,7 @@ var example = `
var bookName string
func preRun(cmd *cobra.Command, args []string) error {
func preRun(cmd *command.Command, args []string) error {
if len(args) != 1 {
return errors.New("Incorrect number of argument")
}
@ -52,8 +52,8 @@ func preRun(cmd *cobra.Command, args []string) error {
}
// NewCmd returns a new remove command
func NewCmd(ctx context.DnoteCtx) *cobra.Command {
cmd := &cobra.Command{
func NewCmd(ctx context.DnoteCtx) *command.Command {
cmd := &command.Command{
Use: "find",
Short: "Find notes by keywords",
Aliases: []string{"f"},
@ -154,7 +154,7 @@ func doQuery(ctx context.DnoteCtx, query, bookName string) (*sql.Rows, error) {
}
func newRun(ctx context.DnoteCtx) infra.RunEFunc {
return func(cmd *cobra.Command, args []string) error {
return func(cmd *command.Command, args []string) error {
phrase, err := escapePhrase(args[0])
if err != nil {
return errors.Wrap(err, "escaping phrase")

View file

@ -24,6 +24,7 @@ import (
"strconv"
"github.com/dnote/dnote/pkg/cli/client"
"github.com/dnote/dnote/pkg/cli/command"
"github.com/dnote/dnote/pkg/cli/consts"
"github.com/dnote/dnote/pkg/cli/context"
"github.com/dnote/dnote/pkg/cli/database"
@ -31,7 +32,6 @@ import (
"github.com/dnote/dnote/pkg/cli/log"
"github.com/dnote/dnote/pkg/cli/ui"
"github.com/pkg/errors"
"github.com/spf13/cobra"
)
var example = `
@ -40,8 +40,8 @@ var example = `
var usernameFlag, passwordFlag string
// NewCmd returns a new login command
func NewCmd(ctx context.DnoteCtx) *cobra.Command {
cmd := &cobra.Command{
func NewCmd(ctx context.DnoteCtx) *command.Command {
cmd := &command.Command{
Use: "login",
Short: "Login to dnote server",
Example: example,
@ -49,8 +49,11 @@ func NewCmd(ctx context.DnoteCtx) *cobra.Command {
}
f := cmd.Flags()
f.StringVarP(&usernameFlag, "username", "u", "", "email address for authentication")
f.StringVarP(&passwordFlag, "password", "p", "", "password for authentication")
f.String("username", "", "email address for authentication")
f.StringVar(&usernameFlag, "u", "", "email address for authentication")
f.String("password", "", "password for authentication")
f.StringVar(&passwordFlag, "p", "", "password for authentication")
return cmd
}
@ -150,7 +153,7 @@ func getGreeting(ctx context.DnoteCtx) string {
}
func newRun(ctx context.DnoteCtx) infra.RunEFunc {
return func(cmd *cobra.Command, args []string) error {
return func(cmd *command.Command, args []string) error {
greeting := getGreeting(ctx)
log.Plain(greeting)

View file

@ -22,13 +22,13 @@ import (
"database/sql"
"github.com/dnote/dnote/pkg/cli/client"
"github.com/dnote/dnote/pkg/cli/command"
"github.com/dnote/dnote/pkg/cli/consts"
"github.com/dnote/dnote/pkg/cli/context"
"github.com/dnote/dnote/pkg/cli/database"
"github.com/dnote/dnote/pkg/cli/infra"
"github.com/dnote/dnote/pkg/cli/log"
"github.com/pkg/errors"
"github.com/spf13/cobra"
)
// ErrNotLoggedIn is an error for logging out when not logged in
@ -38,8 +38,8 @@ var example = `
dnote logout`
// NewCmd returns a new logout command
func NewCmd(ctx context.DnoteCtx) *cobra.Command {
cmd := &cobra.Command{
func NewCmd(ctx context.DnoteCtx) *command.Command {
cmd := &command.Command{
Use: "logout",
Short: "Logout from the server",
Example: example,
@ -83,7 +83,7 @@ func Do(ctx context.DnoteCtx) error {
}
func newRun(ctx context.DnoteCtx) infra.RunEFunc {
return func(cmd *cobra.Command, args []string) error {
return func(cmd *command.Command, args []string) error {
err := Do(ctx)
if err == ErrNotLoggedIn {
log.Error("not logged in\n")

View file

@ -23,11 +23,11 @@ import (
"fmt"
"strings"
"github.com/dnote/dnote/pkg/cli/command"
"github.com/dnote/dnote/pkg/cli/context"
"github.com/dnote/dnote/pkg/cli/infra"
"github.com/dnote/dnote/pkg/cli/log"
"github.com/pkg/errors"
"github.com/spf13/cobra"
)
var example = `
@ -43,7 +43,7 @@ var deprecationWarning = `and "view" will replace it in the future version.
Run "dnote view --help" for more information.
`
func preRun(cmd *cobra.Command, args []string) error {
func preRun(cmd *command.Command, args []string) error {
if len(args) > 1 {
return errors.New("Incorrect number of argument")
}
@ -52,8 +52,8 @@ func preRun(cmd *cobra.Command, args []string) error {
}
// NewCmd returns a new ls command
func NewCmd(ctx context.DnoteCtx) *cobra.Command {
cmd := &cobra.Command{
func NewCmd(ctx context.DnoteCtx) *command.Command {
cmd := &command.Command{
Use: "ls <book name?>",
Aliases: []string{"l", "notes"},
Short: "List all notes",
@ -68,7 +68,7 @@ func NewCmd(ctx context.DnoteCtx) *cobra.Command {
// NewRun returns a new run function for ls
func NewRun(ctx context.DnoteCtx, nameOnly bool) infra.RunEFunc {
return func(cmd *cobra.Command, args []string) error {
return func(cmd *command.Command, args []string) error {
if len(args) == 0 {
if err := printBooks(ctx, nameOnly); err != nil {
return errors.Wrap(err, "viewing books")

View file

@ -22,6 +22,7 @@ import (
"fmt"
"strconv"
"github.com/dnote/dnote/pkg/cli/command"
"github.com/dnote/dnote/pkg/cli/context"
"github.com/dnote/dnote/pkg/cli/database"
"github.com/dnote/dnote/pkg/cli/infra"
@ -30,7 +31,6 @@ import (
"github.com/dnote/dnote/pkg/cli/ui"
"github.com/dnote/dnote/pkg/cli/utils"
"github.com/pkg/errors"
"github.com/spf13/cobra"
)
var bookFlag string
@ -45,8 +45,8 @@ var example = `
`
// NewCmd returns a new remove command
func NewCmd(ctx context.DnoteCtx) *cobra.Command {
cmd := &cobra.Command{
func NewCmd(ctx context.DnoteCtx) *command.Command {
cmd := &command.Command{
Use: "remove <note id|book name>",
Short: "Remove a note or a book",
Aliases: []string{"rm", "d", "delete"},
@ -59,12 +59,12 @@ func NewCmd(ctx context.DnoteCtx) *cobra.Command {
f.StringVarP(&bookFlag, "book", "b", "", "The book name to delete")
f.BoolVarP(&yesFlag, "yes", "y", false, "Assume yes to the prompts and run in non-interactive mode")
f.MarkDeprecated("book", "Pass the book name as an argument. e.g. `dnote rm book_name`")
// f.MarkDeprecated("book", "Pass the book name as an argument. e.g. `dnote rm book_name`")
return cmd
}
func preRun(cmd *cobra.Command, args []string) error {
func preRun(cmd *command.Command, args []string) error {
if len(args) != 1 && len(args) != 2 {
return errors.New("Incorrect number of argument")
}
@ -81,7 +81,7 @@ func maybeConfirm(message string, defaultValue bool) (bool, error) {
}
func newRun(ctx context.DnoteCtx) infra.RunEFunc {
return func(cmd *cobra.Command, args []string) error {
return func(cmd *command.Command, args []string) error {
// DEPRECATED: Remove in 1.0.0
if bookFlag != "" {
if err := runBook(ctx, bookFlag); err != nil {

View file

@ -19,21 +19,18 @@
package root
import (
"github.com/spf13/cobra"
"github.com/dnote/dnote/pkg/cli/command"
)
var root = &cobra.Command{
var root = &command.Command{
Use: "dnote",
Short: "Dnote - a simple command line notebook",
SilenceErrors: true,
SilenceUsage: true,
CompletionOptions: cobra.CompletionOptions{
DisableDefaultCmd: true,
},
}
// Register adds a new command
func Register(cmd *cobra.Command) {
func Register(cmd *command.Command) {
root.AddCommand(cmd)
}

View file

@ -23,6 +23,7 @@ import (
"fmt"
"github.com/dnote/dnote/pkg/cli/client"
"github.com/dnote/dnote/pkg/cli/command"
"github.com/dnote/dnote/pkg/cli/consts"
"github.com/dnote/dnote/pkg/cli/context"
"github.com/dnote/dnote/pkg/cli/database"
@ -31,7 +32,6 @@ import (
"github.com/dnote/dnote/pkg/cli/migrate"
"github.com/dnote/dnote/pkg/cli/upgrade"
"github.com/pkg/errors"
"github.com/spf13/cobra"
)
const (
@ -45,8 +45,8 @@ var example = `
var isFullSync bool
// NewCmd returns a new sync command
func NewCmd(ctx context.DnoteCtx) *cobra.Command {
cmd := &cobra.Command{
func NewCmd(ctx context.DnoteCtx) *command.Command {
cmd := &command.Command{
Use: "sync",
Aliases: []string{"s"},
Short: "Sync data with the server",
@ -886,7 +886,7 @@ func saveSyncState(tx *database.DB, serverTime int64, serverMaxUSN int) error {
}
func newRun(ctx context.DnoteCtx) infra.RunEFunc {
return func(cmd *cobra.Command, args []string) error {
return func(cmd *command.Command, args []string) error {
if ctx.SessionKey == "" {
return errors.New("not logged in")
}

View file

@ -21,17 +21,17 @@ package version
import (
"fmt"
"github.com/dnote/dnote/pkg/cli/command"
"github.com/dnote/dnote/pkg/cli/context"
"github.com/spf13/cobra"
)
// NewCmd returns a new version command
func NewCmd(ctx context.DnoteCtx) *cobra.Command {
cmd := &cobra.Command{
func NewCmd(ctx context.DnoteCtx) *command.Command {
cmd := &command.Command{
Use: "version",
Short: "Print the version number of Dnote",
Long: "Print the version number of Dnote",
Run: func(cmd *cobra.Command, args []string) {
Run: func(cmd *command.Command, args []string) {
fmt.Printf("dnote %s\n", ctx.Version)
},
}

View file

@ -19,10 +19,10 @@
package view
import (
"github.com/dnote/dnote/pkg/cli/command"
"github.com/dnote/dnote/pkg/cli/context"
"github.com/dnote/dnote/pkg/cli/infra"
"github.com/pkg/errors"
"github.com/spf13/cobra"
"github.com/dnote/dnote/pkg/cli/cmd/cat"
"github.com/dnote/dnote/pkg/cli/cmd/ls"
@ -43,7 +43,7 @@ var example = `
var nameOnly bool
var contentOnly bool
func preRun(cmd *cobra.Command, args []string) error {
func preRun(cmd *command.Command, args []string) error {
if len(args) > 2 {
return errors.New("Incorrect number of argument")
}
@ -52,8 +52,9 @@ func preRun(cmd *cobra.Command, args []string) error {
}
// NewCmd returns a new view command
func NewCmd(ctx context.DnoteCtx) *cobra.Command {
cmd := &cobra.Command{
func NewCmd(ctx context.DnoteCtx) *command.Command {
cmd := &command.Command{
Name: "view",
Use: "view <book name?> <note index?>",
Aliases: []string{"v"},
Short: "List books, notes or view a content",
@ -70,7 +71,7 @@ func NewCmd(ctx context.DnoteCtx) *cobra.Command {
}
func newRun(ctx context.DnoteCtx) infra.RunEFunc {
return func(cmd *cobra.Command, args []string) error {
return func(cmd *command.Command, args []string) error {
var run infra.RunEFunc
if len(args) == 0 {

183
pkg/cli/command/command.go Normal file
View file

@ -0,0 +1,183 @@
package command
import (
"fmt"
"os"
"github.com/dnote/dnote/pkg/cli/log"
"github.com/pkg/errors"
flag "github.com/spf13/pflag"
)
type Command struct {
// Name is the name of the command. This is used to find the subcommand
// and is case sensitive.
Name string
// flags is a FlagSet which can parse and hold flags and their values.
flags *flag.FlagSet
// args is a list of arments
args []string
// RunE is a function that contains the logic for the command
RunE func(cmd *Command, args []string) error
// commands is the list of subcommands that this command has.
commands []*Command
// Parent is a pointer to the parent command, if any, of which the command
// is a subcommand.
Parent *Command
Use string
Short string
SilenceErrors bool
SilenceUsage bool
Example string
Aliases []string
PreRunE func(cmd *Command, args []string) error
Deprecated string
Long string
Run func(cmd *Command, args []string)
}
func (c *Command) HelpFunc() {
err := tmpl(os.Stdout, c.HelpTemplate(), c)
if err != nil {
fmt.Println(err)
}
}
// Flags returns a flag set for the command. If not initialized yet, it initializes
// one and returns the result.
func (c *Command) Flags() *flag.FlagSet {
if c.flags == nil {
c.flags = flag.NewFlagSet(c.Name, flag.ContinueOnError)
}
return c.flags
}
// ParseFlags parses the given slice of arguments using the flag set of the command.
func (c *Command) ParseFlags(args []string) error {
err := c.Flags().Parse(args)
if err != nil {
return errors.Wrap(err, "Error parsing flags")
}
return nil
}
// Root returns the root command of the given command.
func (c *Command) Root() *Command {
if c.Parent != nil {
return c.Parent.Root()
}
return c
}
// setArgs sets the arguments for the command. It is useful while writing tests.
func (c *Command) setArgs(args []string) {
c.args = args
}
// Args returns the argument for the command. By default, os.Args[1:] is used.
func (c *Command) Args() []string {
args := c.args
if c.args == nil {
args = os.Args[1:]
}
return args
}
// Execute runs the root command. It is meant to be called on the root command.
func (c *Command) Execute() error {
// Call Execute on the root command
if c.Parent != nil {
return c.Root().Execute()
}
args := c.Args()
log.Debug("root command received arguments: %s\n", args)
cmd, args := c.findCommand(args)
if cmd == nil {
// not found. show suggestion
return nil
}
if err := cmd.execute(args); err != nil {
if errors.Cause(err) == errNotRunnable {
cmd.Help()
}
}
return nil
}
var errNotRunnable = errors.New("Command is not runnable.")
// execute runs the command.
func (c *Command) execute(args []string) error {
log.Debug("command '%s' called with arguments: %s\n", c.Name, args)
if err := c.ParseFlags(args); err != nil {
return err
}
nonFlagArgs := c.Flags().Args()
log.Debug("command '%s' called with non-flag arguments: %s\n", c.Name, nonFlagArgs)
if c.RunE == nil {
return errNotRunnable
}
if err := c.RunE(c, nonFlagArgs); err != nil {
return err
}
return nil
}
// hasAlias checks whether the command has the given alias.
func (c *Command) hasAlias(targetAlias string) bool {
for _, alias := range c.Aliases {
if alias == targetAlias {
return true
}
}
return false
}
// findCommand finds and returns an appropriate subcommand to be called, based
// on the given slice of arguments. It also returns a slice of arguments with which
// the subcommand should be called.
func (c *Command) findCommand(args []string) (*Command, []string) {
if len(args) == 0 {
return c, args
}
name := args[0]
log.Debug("sub-command: '%s'\n", name)
for _, cmd := range c.commands {
if cmd.Name == name || cmd.hasAlias(name) {
return cmd, args[1:]
}
}
return c, args
}
// AddCommand adds the given command as a subcommand.
func (c *Command) AddCommand(cmd *Command) {
cmd.Parent = c
c.commands = append(c.commands, cmd)
}

View file

@ -0,0 +1,105 @@
package command
import (
"testing"
"github.com/dnote/dnote/pkg/assert"
)
func TestAddCommand(t *testing.T) {
cmd := Command{Name: "root command"}
assert.Equal(t, len(cmd.commands), 0, "Commands length mismatch")
subCommand1 := Command{Name: "foo"}
cmd.AddCommand(&subCommand1)
assert.Equal(t, subCommand1.Parent, &cmd, "subCommand1 Parent mismatch")
assert.Equal(t, len(cmd.commands), 1, "Commands length mismatch")
assert.Equal(t, cmd.commands[0], &subCommand1, "commands[0] mismatch")
subCommand2 := Command{Name: "bar"}
cmd.AddCommand(&subCommand2)
assert.Equal(t, len(cmd.commands), 2, "Commands length mismatch")
assert.Equal(t, subCommand2.Parent, &cmd, "subCommand2 Parent mismatch")
assert.Equal(t, cmd.commands[0], &subCommand1, "commands[0] mismatch")
assert.Equal(t, cmd.commands[1], &subCommand2, "commands[1] mismatch")
}
func TestHasAlias(t *testing.T) {
cmd := Command{
Name: "foo",
Aliases: []string{"f", "bar"},
}
assert.Equal(t, cmd.hasAlias("f"), true, "Command should have 'f' alias")
assert.Equal(t, cmd.hasAlias("F"), false, "Command should not have 'F' alias")
assert.Equal(t, cmd.hasAlias("bar"), true, "Command should have 'bar' alias")
assert.Equal(t, cmd.hasAlias("BAR"), false, "Command should have 'BAR' alias")
assert.Equal(t, cmd.hasAlias("baz"), false, "Command should not have 'baz' alias")
assert.Equal(t, cmd.hasAlias(""), false, "Command should not have an empty alias")
}
func TestHasAlias_withoutAlias(t *testing.T) {
cmd := Command{
Name: "foo",
}
assert.Equal(t, cmd.hasAlias("f"), false, "Command should not have any alias")
assert.Equal(t, cmd.hasAlias(""), false, "Command should not have any alias")
}
func TestCommandRoot(t *testing.T) {
subCommand2 := Command{
Name: "baz",
}
subCommand1 := Command{
Name: "bar",
commands: []*Command{
&subCommand2,
},
}
cmd := Command{
Name: "foo",
commands: []*Command{
&subCommand1,
},
}
subCommand1.Parent = &cmd
subCommand2.Parent = &subCommand1
assert.Equal(t, cmd.Root(), &cmd, "Command should already be a root")
assert.Equal(t, subCommand1.Root(), &cmd, "subCommand1 root mismatch")
assert.Equal(t, subCommand2.Root(), &cmd, "subCommand2 root mismatch")
}
func TestFindSubcommand(t *testing.T) {
subCommand1 := Command{
Name: "bar",
Aliases: []string{"quz"},
}
subCommand2 := Command{
Name: "baz",
}
cmd := Command{
Name: "foo",
commands: []*Command{
&subCommand1,
&subCommand2,
},
}
assert.Equal(t, cmd.findSubCommand("bar"), &subCommand1, "Subcommand 'bar' mismatch")
assert.Equal(t, cmd.findSubCommand("baz"), &subCommand2, "Subcommand 'baz' mismatch")
// Should match an alias
assert.Equal(t, cmd.findSubCommand("quz"), &subCommand1, "Subcommand 'quz' mismatch")
// Should not match if not exists
assert.Equal(t, cmd.findSubCommand("qux"), (*Command)(nil), "Subcommand 'qux' mismatch")
// Should not match itself
assert.Equal(t, subCommand1.findSubCommand("bar"), (*Command)(nil), "Subcommand 'bar' mismatch")
}
func executeCommand(cmd *Command, args ...string) error {
cmd.setArgs(args)
return cmd.Execute()
}

View file

@ -28,6 +28,7 @@ import (
"strconv"
"time"
"github.com/dnote/dnote/pkg/cli/command"
"github.com/dnote/dnote/pkg/cli/config"
"github.com/dnote/dnote/pkg/cli/consts"
"github.com/dnote/dnote/pkg/cli/context"
@ -38,11 +39,10 @@ import (
"github.com/dnote/dnote/pkg/cli/utils"
"github.com/dnote/dnote/pkg/clock"
"github.com/pkg/errors"
"github.com/spf13/cobra"
)
// RunEFunc is a function type of dnote commands
type RunEFunc func(*cobra.Command, []string) error
type RunEFunc func(*command.Command, []string) error
func checkLegacyDBPath() (string, bool) {
legacyDnoteDir := getLegacyDnotePath(dirs.Home)