You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

113 lines
2.2 KiB

package console
import (
"errors"
"fmt"
"regexp"
"sort"
"sync"
)
const (
CommandHelp = "help"
CommandList = "list"
)
//nolint:gochecknoglobals
var (
commandsMu sync.RWMutex
commands = make(map[string]*Command)
findCommand = regexp.MustCompile("([^:]+|)")
)
// MustRegister register command or panic if err.
func MustRegister(cmd *Command) {
if err := Register(cmd); err != nil {
panic(err)
}
}
// Register makes a command available execute in app. If Register is called twice with the same name or if driver is nil, return error.
func Register(cmd *Command) error {
if cmd == nil {
return ErrCommandNil
}
if _, err := Find(cmd.Name); !errors.Is(err, ErrNotFound) {
return fmt.Errorf("%w: command %s", ErrCommandDuplicate, cmd.Name)
}
register(cmd)
return nil
}
func register(cmd *Command) {
commandsMu.Lock()
defer commandsMu.Unlock()
if cmd != nil && cmd.Name != "" {
commands[cmd.Name] = cmd
}
}
// Commands returns a sorted list of the names of the registered commands.
func Commands() []string {
commandsMu.RLock()
defer commandsMu.RUnlock()
return commandNames()
}
func commandNames() []string {
names := make([]string, 0, len(commands))
for name := range commands {
names = append(names, name)
}
sort.Strings(names)
return names
}
// Find command by name, tries to find the best match if you give it an abbreviation of a name.
func Find(name string) (*Command, error) {
commandsMu.RLock()
defer commandsMu.RUnlock()
if cmd, ok := commands[name]; ok {
return cmd, nil
}
nameRegexp := findCommand.ReplaceAllStringFunc(name, func(in string) string {
return in + "[^:]*"
})
findCommands := make([]*Command, 0)
cmdRegexp, err := regexp.Compile("^" + nameRegexp + "$")
if err != nil {
return nil, fmt.Errorf("find by regexp:%w", err)
}
for name := range commands {
if !commands[name].Hidden && cmdRegexp.MatchString(name) {
findCommands = append(findCommands, commands[name])
}
}
if len(findCommands) == 1 {
return findCommands[0], nil
}
if len(findCommands) > 1 {
names := make([]string, len(findCommands))
for i := range findCommands {
names[i] = findCommands[i].Name
}
return nil, AlternativesError{Alt: names, Err: ErrNotFound}
}
return nil, ErrNotFound
}