command.go 2.5 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283
  1. package commands
  2. import (
  3. "flag"
  4. )
  5. // CommandFlags is called before help or command execution to parse command line flags.
  6. // It should return a flag.FlagSet instance and a pointer to the struct that will contain parsed flags
  7. type CommandFlags func() (*flag.FlagSet, any)
  8. // CommandCallback is called when your command should execute.
  9. // fs will be a pointer to the struct provided by Command.Flags callback, if there was one. -h and -help are reserved
  10. // and handled automatically for you.
  11. // a will be any unconsumed arguments, if no Command.Flags was available this will be all the flags passed in.
  12. // w is the writer to use when sending messages back to the client.
  13. // An error returned by the callback is handed back to whoever dispatched the command, which reports a failure: exit
  14. // status 1 over ssh. The error is not shown to the user. For a failure the user caused or can act on, write the
  15. // message and return it as a *Failure, which Fail does in one step; the surface then reports the failure without
  16. // logging what the user was already told. Any other error is logged by the surface as well.
  17. type CommandCallback func(fs any, a []string, w StringWriter) error
  18. type Command struct {
  19. Name Name
  20. ShortDescription string
  21. Help string
  22. Flags CommandFlags
  23. Callback CommandCallback
  24. }
  25. // Failure is the error a command returns for a failure it has already explained to the user:
  26. // the message it wrote is the whole story. A surface reports it as a failure and does not
  27. // log it.
  28. type Failure struct {
  29. Msg string
  30. }
  31. func (f *Failure) Error() string {
  32. return f.Msg
  33. }
  34. // Fail writes msg to w and returns it as a *Failure, so a command reports a failure to the
  35. // user and to its surface in one line. If the write itself fails, that error is returned
  36. // instead: the user was not told anything.
  37. func Fail(w StringWriter, msg string) error {
  38. if err := w.WriteLine(msg); err != nil {
  39. return err
  40. }
  41. return &Failure{Msg: msg}
  42. }
  43. func execCommand(c *Command, args []string, w StringWriter) error {
  44. var (
  45. fl *flag.FlagSet
  46. fs any
  47. )
  48. if c.Flags != nil {
  49. fl, fs = c.Flags()
  50. if fl != nil {
  51. // SetOutput() here in case fl.Parse dumps usage.
  52. fl.SetOutput(w.GetWriter())
  53. err := fl.Parse(args)
  54. if err != nil {
  55. // fl.Parse has dumped error information to the user via the w writer.
  56. return err
  57. }
  58. args = fl.Args()
  59. }
  60. }
  61. return c.Callback(fs, args, w)
  62. }
  63. func checkHelpArgs(args []string) bool {
  64. for _, a := range args {
  65. if a == "-h" || a == "-help" {
  66. return true
  67. }
  68. }
  69. return false
  70. }