package main import ( "context" "fmt" "os" "strings" "github.com/mediocregopher/mediocre-go-lib/v2/mlog" "github.com/spf13/pflag" ) // subCmdCtx contains all information available to a subCmd's do method. type subCmdCtx struct { subCmd subCmd // the subCmd itself args []string // command-line arguments, excluding the subCmd itself. subCmdNames []string // names of subCmds so far, including this one ctx context.Context logger *mlog.Logger } type subCmd struct { name string descr string checkLock bool do func(subCmdCtx) error } func (ctx subCmdCtx) usagePrefix() string { subCmdNamesStr := strings.Join(ctx.subCmdNames, " ") if subCmdNamesStr != "" { subCmdNamesStr += " " } return fmt.Sprintf("\nUSAGE: %s %s", os.Args[0], subCmdNamesStr) } func (ctx subCmdCtx) flagSet(withPassthrough bool) *pflag.FlagSet { flags := pflag.NewFlagSet(ctx.subCmd.name, pflag.ExitOnError) flags.Usage = func() { var passthroughStr string if withPassthrough { passthroughStr = " [--] [args...]" } fmt.Fprintf( os.Stderr, "%s[-h|--help] [%s flags...]%s\n\n", ctx.usagePrefix(), ctx.subCmd.name, passthroughStr, ) fmt.Fprintf(os.Stderr, "%s FLAGS:\n\n", strings.ToUpper(ctx.subCmd.name)) fmt.Fprintln(os.Stderr, flags.FlagUsages()) os.Stderr.Sync() os.Exit(2) } return flags } func (ctx subCmdCtx) doSubCmd(subCmds ...subCmd) error { printUsageExit := func(subCmdName string) { fmt.Fprintf(os.Stderr, "unknown sub-command %q\n", subCmdName) fmt.Fprintf( os.Stderr, "%s [-h|--help] [sub-command flags...]\n", ctx.usagePrefix(), ) fmt.Fprintf(os.Stderr, "\nSUB-COMMANDS:\n\n") for _, subCmd := range subCmds { fmt.Fprintf(os.Stderr, " %s\t%s\n", subCmd.name, subCmd.descr) } fmt.Fprintf(os.Stderr, "\n") os.Stderr.Sync() os.Exit(2) } args := ctx.args if len(args) == 0 { printUsageExit("") } subCmdsMap := map[string]subCmd{} for _, subCmd := range subCmds { subCmdsMap[subCmd.name] = subCmd } subCmdName, args := args[0], args[1:] subCmd, ok := subCmdsMap[subCmdName] if !ok { printUsageExit(subCmdName) } if subCmd.checkLock { if err := assertLock(); err != nil { return fmt.Errorf("checking lock file: %w", err) } } err := subCmd.do(subCmdCtx{ subCmd: subCmd, args: args, subCmdNames: append(ctx.subCmdNames, subCmdName), ctx: ctx.ctx, logger: ctx.logger, }) if err != nil { return err } return nil }