github.com/WindomZ/go-commander@v1.2.2/command.go (about)

     1  package commander
     2  
     3  import (
     4  	"fmt"
     5  	"os"
     6  	"path"
     7  	"regexp"
     8  	"strings"
     9  )
    10  
    11  // _Command Command line command implementation
    12  type _Command struct {
    13  	actor
    14  	usage      string              // api set usage
    15  	root       bool                // root command
    16  	clone      bool                // clone command for new help message line
    17  	desc       string              // description
    18  	annotation map[string][]string // annotation, like 'try', 'examples', etc.
    19  	arguments  _Arguments          // parse arguments from usage
    20  	commands   _Commands           // api set subcommands
    21  	options    _Options            // api set options
    22  	last       interface{}         // the last defined object
    23  	doc        string              // define help message
    24  }
    25  
    26  func newCommand(root bool) *_Command {
    27  	return &_Command{
    28  		root: root,
    29  	}
    30  }
    31  
    32  func (c *_Command) init() *_Command {
    33  	if !c.Valid() {
    34  		var name string
    35  		if len(os.Args) != 0 {
    36  			name = os.Args[0]
    37  		} else if dir, err := os.Getwd(); err == nil {
    38  			name = path.Base(dir)
    39  		}
    40  		if name = defineCommand(name); len(name) == 0 {
    41  			panicError("root command should not be empty")
    42  		}
    43  		c.Usage(name)
    44  	}
    45  	return c
    46  }
    47  
    48  func (c _Command) isRoot() bool {
    49  	return c.root && !c.clone
    50  }
    51  
    52  func (c *_Command) Usage(usage string, args ...interface{}) Commander {
    53  	if len(usage) != 0 {
    54  		c.usage = strings.TrimSpace(usage)
    55  		c.regexpNames()
    56  		c.regexpArguments()
    57  	}
    58  	if len(args) >= 1 {
    59  		c.desc, _ = args[0].(string)
    60  	}
    61  	if len(args) >= 2 {
    62  		c.setAction(args[1])
    63  	}
    64  	return c
    65  }
    66  
    67  func (c *_Command) regexpNames() {
    68  	c.names = regexpCommand(c.usage)
    69  }
    70  
    71  func (c *_Command) regexpArguments() {
    72  	c.arguments.Set(c.usage)
    73  }
    74  
    75  func (c _Command) Valid() bool {
    76  	return len(c.names) != 0 && len(c.usage) != 0
    77  }
    78  
    79  func (c _Command) Name() string {
    80  	if len(c.names) == 0 {
    81  		return ""
    82  	}
    83  	name := c.names[0]
    84  	if len(c.names) > 1 {
    85  		name = fmt.Sprintf("(%s)", strings.Join(c.names, "|"))
    86  	}
    87  	return name
    88  }
    89  
    90  func (c *_Command) Doc(doc string) Commander {
    91  	c.doc = doc
    92  	return c.init()
    93  }
    94  
    95  func (c *_Command) Version(ver string) Commander {
    96  	return c.init()
    97  }
    98  
    99  func (c _Command) ShowVersion() string {
   100  	return ""
   101  }
   102  
   103  func (c *_Command) Description(desc string) Commander {
   104  	if c.init().last != nil {
   105  		switch obj := c.last.(type) {
   106  		//case *_Command:
   107  		case *_Option:
   108  			obj.Description(desc)
   109  			return c
   110  		}
   111  	}
   112  	c.desc = desc
   113  	return c
   114  }
   115  
   116  func (c *_Command) Annotation(title string, contents []string) Commander {
   117  	if c.annotation == nil {
   118  		c.annotation = make(map[string][]string)
   119  	}
   120  	c.annotation[title] = contents
   121  	return c.init()
   122  }
   123  
   124  func (c *_Command) addCommand(cmd *_Command) bool {
   125  	if c.init().Valid() && cmd.Valid() {
   126  		for _, _cmd := range c.commands {
   127  			_cmd.addExcludeKeys(cmd.getIncludeKeys())
   128  		}
   129  		c.commands = append(c.commands, cmd)
   130  		return true
   131  	} else {
   132  		panicError("command invalid format:", cmd)
   133  	}
   134  	return false
   135  }
   136  
   137  func (c *_Command) Command(usage string, args ...interface{}) (commander Commander) {
   138  	if param := firstParameter(usage); isArgument(param) {
   139  		commander = c.LineArgument(usage, args...)
   140  		goto SetLast
   141  	} else if isOption(param) {
   142  		commander = c.LineOption(usage, args...)
   143  		return
   144  	} else if c.clone {
   145  		usage = c.usage + " " + usage
   146  	} else if c.Valid() {
   147  		cmd := newCommand(false)
   148  		cmd.Usage(usage, args...)
   149  		cmd.addIncludeKeys(cmd.names)
   150  		c.addCommand(cmd)
   151  		commander = cmd
   152  		goto SetLast
   153  	}
   154  	commander = c.Usage(usage, args...)
   155  SetLast:
   156  	c.last = commander
   157  	return
   158  }
   159  
   160  func (c *_Command) Aliases(aliases []string) Commander {
   161  	if c.init().last != nil {
   162  		switch obj := c.last.(type) {
   163  		//case *_Command:
   164  		case *_Option:
   165  			obj.Aliases(aliases)
   166  			return c
   167  		}
   168  	}
   169  	name := c.Name()
   170  	c.names = append(c.names, aliases...)
   171  	c.usage = replaceCommand(c.usage, name, c.Name())
   172  	return c
   173  }
   174  
   175  func (c *_Command) addOption(line bool, usage string, args ...interface{}) (opt *_Option) {
   176  	opt = newOption(usage, args...)
   177  	opt.line = line
   178  	opt.break_off = line
   179  	if opt.Valid() {
   180  		c.init().options = append(c.options, opt)
   181  	}
   182  	c.last = opt
   183  	return opt
   184  }
   185  
   186  func (c *_Command) Option(usage string, args ...interface{}) Commander {
   187  	if opt := c.addOption(false, usage, args...); !opt.Valid() {
   188  		panicError("option invalid format:", opt)
   189  	}
   190  	return c
   191  }
   192  
   193  func (c *_Command) Line(usage string, args ...interface{}) *_Command {
   194  	cmd := newCommand(c.root)
   195  	cmd.Usage(usage, args...)
   196  	cmd.clone = true
   197  	cmd.ignore = true
   198  	return cmd
   199  }
   200  
   201  func (c *_Command) LineArgument(usage string, args ...interface{}) Commander {
   202  	usage = c.Name() + " " + usage
   203  	cmd := c.Line(usage, args...)
   204  	if cmd.arguments.IsEmpty() {
   205  		return cmd
   206  	}
   207  	cmd.ignore = false
   208  	cmd.addIncludeKeys(cmd.arguments.Get())
   209  	c.addCommand(cmd)
   210  	return cmd
   211  }
   212  
   213  func (c *_Command) LineOption(usage string, args ...interface{}) Commander {
   214  	cmd := c.Line(c.usage, args...)
   215  	opt := cmd.addOption(true, usage, args...)
   216  	if cmd.options.IsEmpty() {
   217  		return cmd
   218  	}
   219  	cmd.addIncludeKeys(opt.Names())
   220  	c.addCommand(cmd)
   221  	return cmd
   222  }
   223  
   224  func (c *_Command) Action(action interface{}, keys ...[]string) Commander {
   225  	if c.init().last != nil {
   226  		switch obj := c.last.(type) {
   227  		//case *_Command:
   228  		case *_Option:
   229  			if c.clone || c.actor.hasAction() {
   230  				obj.actor.Action(action, keys...)
   231  				return c
   232  			}
   233  		}
   234  	}
   235  	c.actor.Action(action, keys...)
   236  	return c
   237  }
   238  
   239  func (c _Command) UsagesString() (r []string) {
   240  	if !c.Valid() {
   241  		return
   242  	}
   243  	str := c.usage
   244  	if len(c.options) != 0 {
   245  		uStrs := c.options.UsagesString(c.arguments.IsEmpty())
   246  		for _, uStr := range uStrs {
   247  			str += " " + uStr
   248  		}
   249  	}
   250  	name := c.Name()
   251  	if !(c.root || c.clone) || str != name {
   252  		r = append(r, str)
   253  	}
   254  	name += " "
   255  	for _, cmd := range c.commands {
   256  		uStrs := cmd.UsagesString()
   257  		for _, uStr := range uStrs {
   258  			if strings.HasPrefix(uStr, name) {
   259  				r = append(r, uStr)
   260  			} else {
   261  				r = append(r, name+uStr)
   262  			}
   263  		}
   264  	}
   265  	return
   266  }
   267  
   268  func (c _Command) OptionsString() (r map[string]string) {
   269  	if !c.Valid() {
   270  		return
   271  	}
   272  	r = c.options.OptionsString()
   273  	opts := c.commands.OptionsString()
   274  	for k, v := range opts {
   275  		r[k] = v
   276  	}
   277  	return
   278  }
   279  
   280  func (c _Command) CommandsString(prefix string) (r []string) {
   281  	if !c.Valid() {
   282  		return
   283  	}
   284  	name := regexp.MustCompile(`[()]`).ReplaceAllString(c.Name(), "")
   285  	if c.root {
   286  		name = prefix
   287  	} else {
   288  		if len(prefix) != 0 {
   289  			name = prefix + " " + name
   290  		}
   291  		if len(c.desc) != 0 {
   292  			r = append(r, Format.Description(name, c.desc))
   293  		}
   294  	}
   295  	r = append(r, c.commands.CommandsString(name)...)
   296  	return
   297  }
   298  
   299  // HelpMessage get string of help message that generated according to the docopt format
   300  func (c _Command) HelpMessage() string {
   301  	if len(c.doc) != 0 {
   302  		return c.doc
   303  	}
   304  
   305  	var hm _HelpMessage
   306  
   307  	// Description
   308  	if len(c.desc) != 0 {
   309  		hm.Description(c.desc)
   310  	}
   311  
   312  	// Usages
   313  	if strs := c.UsagesString(); len(strs) != 0 {
   314  		hm.Title("Usage")
   315  		for _, str := range strs {
   316  			hm.Subtitle(str)
   317  		}
   318  	}
   319  
   320  	// Options
   321  	if opts := c.OptionsString(); len(opts) != 0 {
   322  		strs := sortStringMap(opts)
   323  		hm.Line().Title("Options")
   324  		for _, str := range strs {
   325  			hm.Subtitle(str)
   326  		}
   327  	}
   328  
   329  	// Commands
   330  	if strs := c.CommandsString(""); len(strs) != 0 {
   331  		hm.Line().Title("Commands")
   332  		for _, str := range strs {
   333  			hm.Subtitle(str)
   334  		}
   335  	}
   336  
   337  	// Annotation
   338  	if c.annotation != nil {
   339  		for title, contents := range c.annotation {
   340  			hm.Line().Title(title)
   341  			for _, content := range contents {
   342  				hm.Subtitle(content)
   343  			}
   344  		}
   345  	}
   346  
   347  	return hm.String()
   348  }
   349  
   350  func (c _Command) ShowHelpMessage() string {
   351  	s := c.HelpMessage()
   352  	fmt.Println(s)
   353  	return s
   354  }
   355  
   356  func (c _Command) Parse(args ...[]string) (Context, error) {
   357  	return nil, nil
   358  }
   359  
   360  func (c _Command) run(context Context) _Result {
   361  	if c.root || c.allow(context) {
   362  		if r := c.commands.run(context); r != nil {
   363  			return r
   364  		}
   365  		if r := c.options.run(context); r != nil && r.Break() {
   366  			return r
   367  		}
   368  		return c.actor.run(context, c.isRoot())
   369  	}
   370  	return nil
   371  }
   372  
   373  func (c *_Command) ErrorHandling(f func(error)) Commander {
   374  	return c
   375  }