Skip to content

Commit

Permalink
Merge pull request #7 from MrLYC/develop
Browse files Browse the repository at this point in the history
Add command completion helper
  • Loading branch information
MrLYC authored Mar 6, 2022
2 parents 1ec3e5f + c8c0e16 commit 45a53e0
Show file tree
Hide file tree
Showing 11 changed files with 374 additions and 29 deletions.
24 changes: 12 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
# CMDR
[![test](https://github.com/MrLYC/cmdr/actions/workflows/unittest.yml/badge.svg)](https://github.com/MrLYC/cmdr/actions/workflows/unittest.yml) [![release](https://github.com/MrLYC/cmdr/actions/workflows/release.yml/badge.svg)](https://github.com/MrLYC/cmdr/actions/workflows/main.yml) [![codecov](https://codecov.io/gh/MrLYC/cmdr/branch/main/graph/badge.svg?token=mo4TJP4mQt)](https://codecov.io/gh/MrLYC/cmdr)
[![test](https://github.com/MrLYC/cmdr/actions/workflows/unittest.yml/badge.svg)](https://github.com/MrLYC/cmdr/actions/workflows/unittest.yml) [![release](https://github.com/MrLYC/cmdr/actions/workflows/release.yml/badge.svg)](https://github.com/MrLYC/cmdr/actions/workflows/main.yml) [![codecov](https://codecov.io/gh/MrLYC/cmdr/branch/master/graph/badge.svg?token=mo4TJP4mQt)](https://codecov.io/gh/MrLYC/cmdr)

CMDR is a simple command version management tool that helps you quickly switch from multiple command versions.

## Installation
Download the latest release from [GitHub](https://github.com/MrLYC/cmdr/releases) and make sure it is executable.
Run the following command to install it in your system:
```shell
% /path/to/cmdr setup
```

Check the CMDR version information by running the following command:
```shell
% cmdr version -a
```
1. Download the latest release from [GitHub](https://github.com/MrLYC/cmdr/releases);
2. Make sure the download asset is executable;
3. Run the following command to install the binary:
```shell
% /path/to/cmdr init
```
4. Restart your shell and run the following command to verify the installation:
```shell
% cmdr version
```

## Get Started
To install a new command, run the following command:
Expand All @@ -32,7 +32,7 @@ Use a specified command version:
```

## Upgrade
To upgrade the CMDR, run:
To upgrade the CMDR, just run:
```shell
% cmdr upgrade
```
4 changes: 4 additions & 0 deletions cmd/command/define.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ func init() {
flags.StringP("location", "l", "", "command location")
flags.BoolP("activate", "a", false, "activate command")

helper := utils.NewDefaultCobraCommandCompleteHelper(defineCmd)
cfg := core.GetConfiguration()
utils.PanicOnError("binding flags",

Expand All @@ -48,5 +49,8 @@ func init() {
defineCmd.MarkFlagRequired("location"),

cfg.BindPFlag(core.CfgKeyXCommandDefineActivate, flags.Lookup("activate")),

helper.RegisterNameFunc(),
helper.RegisterVersionFunc(),
)
}
4 changes: 4 additions & 0 deletions cmd/command/install.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ func init() {
flags.StringP("location", "l", "", "command location")
flags.BoolP("activate", "a", false, "activate command")

helper := utils.NewDefaultCobraCommandCompleteHelper(installCmd)
cfg := core.GetConfiguration()
utils.PanicOnError("binding flags",

Expand All @@ -48,5 +49,8 @@ func init() {
installCmd.MarkFlagRequired("location"),

cfg.BindPFlag(core.CfgKeyXCommandInstallActivate, flags.Lookup("activate")),

helper.RegisterNameFunc(),
helper.RegisterVersionFunc(),
)
}
2 changes: 2 additions & 0 deletions cmd/command/list.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,5 +74,7 @@ func init() {
cfg.BindPFlag(core.CfgKeyXCommandListVersion, flags.Lookup("version")),
cfg.BindPFlag(core.CfgKeyXCommandListLocation, flags.Lookup("location")),
cfg.BindPFlag(core.CfgKeyXCommandListActivate, flags.Lookup("activate")),

utils.NewDefaultCobraCommandCompleteHelper(listCmd).RegisterAll(),
)
}
2 changes: 2 additions & 0 deletions cmd/command/remove.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,5 +44,7 @@ func init() {

cfg.BindPFlag(core.CfgKeyXCommandRemoveVersion, flags.Lookup("version")),
removeCmd.MarkFlagRequired("version"),

utils.NewDefaultCobraCommandCompleteHelper(removeCmd).RegisterAll(),
)
}
2 changes: 2 additions & 0 deletions cmd/command/unset.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,5 +26,7 @@ func init() {
utils.PanicOnError("binding flags",
cfg.BindPFlag(core.CfgKeyXCommandUnsetName, flags.Lookup("name")),
unsetCmd.MarkFlagRequired("name"),

utils.NewDefaultCobraCommandCompleteHelper(unsetCmd).RegisterAll(),
)
}
2 changes: 2 additions & 0 deletions cmd/command/use.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,5 +34,7 @@ func init() {

cfg.BindPFlag(core.CfgKeyXCommandUseVersion, flags.Lookup("version")),
useCmd.MarkFlagRequired("version"),

utils.NewDefaultCobraCommandCompleteHelper(useCmd).RegisterAll(),
)
}
11 changes: 11 additions & 0 deletions core/manager/database.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,17 @@ func (f *CommandFilter) Count() (int, error) {
return len(f.commands), nil
}

func (f *CommandFilter) AddCommand(commands ...core.Command) {
for _, command := range commands {
f.commands = append(f.commands, &Command{
Name: command.GetName(),
Version: command.GetVersion(),
Activated: command.GetActivated(),
Location: command.GetLocation(),
})
}
}

func NewCommandFilter(commands []*Command) *CommandFilter {
return &CommandFilter{commands}
}
Expand Down
17 changes: 0 additions & 17 deletions core/utils/cmdr.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,9 @@ package utils

import (
"context"
"fmt"
"strings"

"github.com/pkg/errors"
"github.com/spf13/cobra"

"github.com/mrlyc/cmdr/core"
)
Expand Down Expand Up @@ -89,18 +87,3 @@ func UpgradeCmdr(ctx context.Context, cfg core.Configuration, url, version strin

return nil
}

func RunCobraCommandWith(provider core.CommandProvider, fn func(cfg core.Configuration, manager core.CommandManager) error) func(cmd *cobra.Command, args []string) {
return func(cmd *cobra.Command, args []string) {
cfg := core.GetConfiguration()

manager, err := core.NewCommandManager(provider, cfg)
if err != nil {
ExitOnError("Failed to create command manager", err)
}

defer CallClose(manager)

ExitOnError(fmt.Sprintf("Failed to run command %s", cmd.Name()), fn(cfg, manager))
}
}
205 changes: 205 additions & 0 deletions core/utils/command.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,205 @@
package utils

import (
"fmt"
"strings"
"sync"

"github.com/spf13/cobra"

"github.com/mrlyc/cmdr/core"
)

func RunCobraCommandWith(provider core.CommandProvider, fn func(cfg core.Configuration, manager core.CommandManager) error) func(cmd *cobra.Command, args []string) {
return func(cmd *cobra.Command, args []string) {
cfg := core.GetConfiguration()

manager, err := core.NewCommandManager(provider, cfg)
if err != nil {
ExitOnError("Failed to create command manager", err)
}

defer CallClose(manager)

ExitOnError(fmt.Sprintf("Failed to run command %s", cmd.Name()), fn(cfg, manager))
}
}

type CobraCommandCompleteHelper struct {
managerProvider core.CommandProvider
commands []core.Command
queryOnce sync.Once
cobraCommand *cobra.Command
flagName string
flagVersion string
flagLocation string
flagActivate string
}

func (h *CobraCommandCompleteHelper) updateQuery(query core.CommandQuery) core.CommandQuery {
flags := h.cobraCommand.Flags()
name, err := flags.GetString(h.flagName)
if err == nil && name != "" {
query.WithName(name)
}

version, err := flags.GetString(h.flagVersion)
if err == nil && version != "" {
query.WithVersion(version)
}

location, err := flags.GetString(h.flagLocation)
if err == nil && location != "" {
query.WithLocation(location)
}

activate, err := flags.GetBool(h.flagActivate)
if err == nil && activate {
query.WithActivated(activate)
}

return query
}

func (h *CobraCommandCompleteHelper) getCommands() []core.Command {
logger := core.Logger

h.queryOnce.Do(func() {
manager, err := core.NewCommandManager(h.managerProvider, core.GetConfiguration())
if err != nil {
logger.Debug("Failed to create command manager", map[string]interface{}{
"error": err,
})
return
}

defer manager.Close()

query, err := manager.Query()
if err != nil {
logger.Debug("Failed to create command query", map[string]interface{}{
"error": err,
})
return
}

h.commands, err = h.updateQuery(query).All()
if err != nil {
logger.Debug("Failed to query commands", map[string]interface{}{
"error": err,
})
return
}
})

return h.commands
}

func (h *CobraCommandCompleteHelper) isFlagSet(name string) bool {
flags := h.cobraCommand.Flags()
return flags.Lookup(name) != nil
}

func (h *CobraCommandCompleteHelper) GetNameSlice(prefix string) []string {
commands := h.getCommands()
results := make([]string, 0, len(commands))

for _, command := range commands {
name := command.GetName()
if strings.HasPrefix(name, prefix) {
results = append(results, name)
}
}

return results
}

func (h *CobraCommandCompleteHelper) GetVersionSlice(prefix string) []string {
commands := h.getCommands()
results := make([]string, 0, len(commands))

for _, command := range commands {
version := command.GetVersion()
if strings.HasPrefix(version, prefix) {
results = append(results, version)
}
}

return results
}

func (h *CobraCommandCompleteHelper) GetLocationSlice(prefix string) []string {
commands := h.getCommands()
results := make([]string, 0, len(commands))

for _, command := range commands {
location := command.GetLocation()
if strings.HasPrefix(location, prefix) {
results = append(results, location)
}
}

return results
}

func (h *CobraCommandCompleteHelper) RegisterNameFunc() error {
return h.cobraCommand.RegisterFlagCompletionFunc(
h.flagName,
func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
return h.GetNameSlice(toComplete), cobra.ShellCompDirectiveDefault
},
)
}

func (h *CobraCommandCompleteHelper) RegisterVersionFunc() error {
return h.cobraCommand.RegisterFlagCompletionFunc(
h.flagVersion,
func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
return h.GetVersionSlice(toComplete), cobra.ShellCompDirectiveDefault
},
)
}

func (h *CobraCommandCompleteHelper) RegisterLocationFunc() error {
return h.cobraCommand.RegisterFlagCompletionFunc(
h.flagLocation,
func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
return h.GetLocationSlice(toComplete), cobra.ShellCompDirectiveDefault
},
)
}

func (h *CobraCommandCompleteHelper) RegisterAll() error {
mappings := map[string]func() error{
h.flagName: h.RegisterNameFunc,
h.flagVersion: h.RegisterVersionFunc,
h.flagLocation: h.RegisterLocationFunc,
}

for name, fn := range mappings {
if h.isFlagSet(name) {
err := fn()
if err != nil {
return err
}
}
}

return nil
}

func NewCobraCommandCompleteHelper(cmd *cobra.Command, provider core.CommandProvider) *CobraCommandCompleteHelper {
return &CobraCommandCompleteHelper{
managerProvider: provider,
cobraCommand: cmd,
commands: make([]core.Command, 0),
flagName: "name",
flagVersion: "version",
flagLocation: "location",
flagActivate: "activate",
}
}

func NewDefaultCobraCommandCompleteHelper(cmd *cobra.Command) *CobraCommandCompleteHelper {
return NewCobraCommandCompleteHelper(cmd, core.CommandProviderDefault)
}
Loading

0 comments on commit 45a53e0

Please sign in to comment.