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 }