agent/session/plugin/cli/command.go (187 lines of code) (raw):
// Copyright (c) 2009-present, Alibaba Cloud All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package cli
import (
"fmt"
"strings"
"github.com/aliyun/aliyun_assist_client/agent/session/plugin/i18n"
)
type Command struct {
// Command Name
Name string
// Short is the short description shown in the 'help' output.
Short *i18n.Text
// Long is the long message shown in the 'help <this-command>' output.
Long *i18n.Text
// Syntax for usage
Usage string
// Sample command
Sample string
// Enable unknown flags
EnableUnknownFlag bool
// enable suggest distance,
// disable -1
// 0: default distance
SuggestDistance int
// Hidden command
Hidden bool
// Run, command error will be catch
Run func(ctx *Context, args []string) error
// Help
Help func(ctx *Context, args []string) error
// auto compete
AutoComplete func(ctx *Context, args []string) []string
suggestDistance int
parent *Command
subCommands []*Command
flags *FlagSet
}
func (c *Command) AddSubCommand(cmd *Command) {
cmd.parent = c
c.subCommands = append(c.subCommands, cmd)
}
func (c *Command) Flags() *FlagSet {
if c.flags == nil {
c.flags = NewFlagSet()
}
return c.flags
}
func (c *Command) Execute(ctx *Context, args []string) {
//
// if completion
if ctx.completion != nil {
args = ctx.completion.GetArgs()
}
err := c.executeInner(ctx, args)
if err != nil {
c.processError(ctx, err)
}
}
func (c *Command) GetSubCommand(s string) *Command {
for _, cmd := range c.subCommands {
if cmd.Name == s {
return cmd
}
}
return nil
}
func (c *Command) GetSuggestions(s string) []string {
sr := NewSuggester(s, c.GetSuggestDistance())
for _, cmd := range c.subCommands {
sr.Apply(cmd.Name)
}
return sr.GetResults()
}
func (c *Command) GetSuggestDistance() int {
if c.SuggestDistance < 0 {
return 0
} else if c.SuggestDistance == 0 {
return DefaultSuggestDistance
} else {
return c.SuggestDistance
}
}
func (c *Command) GetUsageWithParent() string {
usage := c.Usage
for p := c.parent; p != nil; p = p.parent {
usage = p.Name + " " + usage
}
return usage
}
func (c *Command) ExecuteComplete(ctx *Context, args []string) {
if strings.HasPrefix(ctx.completion.Current, "-") {
for _, f := range ctx.flags.Flags() {
if f.Hidden {
continue
}
if !strings.HasPrefix("--"+f.Name, ctx.completion.Current) {
continue
}
Printf(ctx.Writer(), "--%s\n", f.Name)
}
} else {
for _, sc := range c.subCommands {
if sc.Hidden {
continue
}
if !strings.HasPrefix(sc.Name, ctx.completion.Current) {
continue
}
Printf(ctx.Writer(), "%s\n", sc.Name)
}
}
}
func (c *Command) executeInner(ctx *Context, args []string) error {
//
// fmt.Printf(">>> Execute Command: %s args=%v\n", c.Name, args)
parser := NewParser(args, ctx)
//
// get next arg
nextArg, _, err := parser.ReadNextArg()
if err != nil {
return err
}
//
// if next arg is help, run help
if nextArg == "help" {
ctx.help = true
return c.executeInner(ctx, parser.GetRemains())
}
//
// if next args is not empty, try find sub commands
if nextArg != "" {
//
// if has sub command, run it
subCommand := c.GetSubCommand(nextArg)
if subCommand != nil {
ctx.EnterCommand(subCommand)
return subCommand.executeInner(ctx, parser.GetRemains())
}
//
// no sub command and command.Run == nil
// raise error
if c.Run == nil {
// c.executeHelp(ctx, args, fmt.Errorf("unknown command: %s", nextArg))
return NewInvalidCommandError(nextArg, ctx)
}
}
// cmd is find by args, try run cmd.Run
// parse remain args
remainArgs, err := parser.ReadAll()
if err != nil {
return fmt.Errorf("parse failed %s", err)
}
//
// check flags
err = ctx.CheckFlags()
if err != nil {
return err
}
if HelpFlag(ctx.Flags()).IsAssigned() {
ctx.help = true
}
callArgs := make([]string, 0)
if nextArg != "" {
callArgs = append(callArgs, nextArg)
}
for _, s := range remainArgs {
if s != "help" {
callArgs = append(callArgs, s)
} else {
ctx.help = true
}
}
if ctx.completion != nil {
if c.AutoComplete != nil {
ss := c.AutoComplete(ctx, callArgs)
for _, s := range ss {
Printf(ctx.Writer(), "%s\n", s)
}
} else {
c.ExecuteComplete(ctx, callArgs)
}
return nil
}
if ctx.help {
c.executeHelp(ctx, callArgs)
return nil
} else if c.Run == nil {
c.executeHelp(ctx, callArgs)
return nil
} else {
return c.Run(ctx, callArgs)
}
}
func (c *Command) processError(ctx *Context, err error) {
Errorf(ctx.Stderr(), "ERROR: %s\n", err.Error())
if e, ok := err.(SuggestibleError); ok {
PrintSuggestions(ctx, i18n.GetLanguage(), e.GetSuggestions())
Exit(2)
return
}
if e, ok := err.(ErrorWithTip); ok {
Noticef(ctx.Stderr(), "\n%s\n", e.GetTip(i18n.GetLanguage()))
Exit(3)
return
}
Exit(1)
}
func (c *Command) executeHelp(ctx *Context, args []string) {
if c.Help != nil {
err := c.Help(ctx, args)
if err != nil {
c.processError(ctx, err)
}
return
}
c.PrintHead(ctx)
c.PrintUsage(ctx)
c.PrintSubCommands(ctx)
c.PrintFlags(ctx)
c.PrintTail(ctx)
}