package cli import ( "context" "errors" "io" "os" "github.com/spf13/pflag" "tsne.dev/hopper/internal/log" ) // Run executes the CLI and returns the process exit code. // // Note: Help paths will call exit directly. func Run(ctx context.Context, args []string) int { main := newMainCommand() err := runCommand(ctx, main, args) if err != nil { var exit exitError if errors.As(err, &exit) { return exit.code } var ev errorVerboser if errors.As(err, &ev) { log.Error("%s\n %s", err.Error(), ev.ErrorVerbose()) } else { log.Error(err.Error()) } return 1 } return 0 } type command interface { Usage() string Run(ctx context.Context, args []string) error } func parseFlags(cmd command, flags *pflag.FlagSet, args []string) { err := flags.Parse(args) if err != nil { if errors.Is(err, pflag.ErrHelp) { log.Print(cmd.Usage()) os.Exit(0) } else { log.Error("%s\n\n%s", err.Error(), cmd.Usage()) os.Exit(2) } } } func runCommand(ctx context.Context, cmd command, args []string) error { err := cmd.Run(ctx, args) if err != nil { var usageErr usageError if errors.As(err, &usageErr) { log.Error("%s\n\n%s", usageErr.msg, cmd.Usage()) os.Exit(2) } return err } return nil } func newFlagset(name string) *pflag.FlagSet { fs := pflag.NewFlagSet(name, pflag.ContinueOnError) fs.SetOutput(io.Discard) fs.Usage = func() {} return fs }