package diary import ( "strconv" "strings" ) // The hidden commands of cobra's shell completion (cobra.ShellCompRequestCmd // and cobra.ShellCompNoDescRequestCmd), named here so diary does not link // cobra; TestHelpOnlyAgreesWithCobra pins them. const ( completeRequestCmd = "__complete" completeNoDescRequestCmd = "__completeNoDesc" ) // helpOnly reports whether args, a binary's command line without the // program name, only ask the binary to describe itself, so no command runs: // // - there is no command word (the first token before "--" that is neither // a flag nor the value of --env, bonfire's rule under which Execute // prints the list): a bare invocation, only global options, -h or // --help; // - the command word is list, help or one of cobra's shell completion // requests (__complete, __completeNoDesc); // - the help flag is set before "--", also after the command word, the // way cobra honours it: --help, --help= read by strconv.ParseBool // the way pflag does (the last occurrence wins, an unparsable value is // ignored), or a single-dash cluster that reaches h (-h, -vh, -nh). // // It is a tolerant pre-scan of the raw arguments, made before plugins boot, // and reads them the way bonfire.PeekGlobals does: it stops at "--", skips // the value of --env and reads a single-dash cluster letter by letter, // stopping at the first letter that is not a global shorthand (v, q, n, h). // It never fails. An unknown or misspelled command word is not help-only // (that is unknowable before boot). It cannot tell a passthrough command // (whose --help reaches its Run) from another, so " --help" // counts; the root's OnGlobals hook (ApplyGlobals) restores the console // levels before that command runs. func helpOnly(args []string) bool { help := false word := "" scan: for i := 0; i < len(args); i++ { a := args[i] switch { case a == "--": break scan case a == "--env": i++ case a == "--help": help = true case strings.HasPrefix(a, "--help="): if v, err := strconv.ParseBool(strings.TrimPrefix(a, "--help=")); err == nil { help = v } case len(a) >= 2 && a[0] == '-' && a[1] != '-': if v, ok := clusterHelp(a[1:]); ok { help = v } case strings.HasPrefix(a, "-"): case word == "": word = a } } switch word { case "", "list", "help", completeRequestCmd, completeNoDescRequestCmd: return true } return help } // clusterHelp reads a shorthand cluster (the letters after the dash) the // way bonfire.PeekGlobals does and reports the help value it sets: ok is // true when the cluster reaches h before a letter that is not a global // shorthand, and -h= (-vh=false) sets the value read by // strconv.ParseBool. func clusterHelp(cluster string) (help, ok bool) { for i := 0; i < len(cluster); i++ { r := cluster[i] if i+1 < len(cluster) && cluster[i+1] == '=' { if r != 'h' { return help, ok } if v, err := strconv.ParseBool(cluster[i+2:]); err == nil { return v, true } return help, ok } switch r { case 'h': help, ok = true, true case 'v', 'q', 'n': default: return help, ok } } return help, ok } // bootQuiet applies the help-only rule of Bootstrap and Setup: with Args // set, no -v and arguments that only ask for help, the setup is installed // with Quiet, which raises only the console channels to warn. func bootQuiet(opts Options) Options { if opts.Args != nil && opts.Verbosity == 0 && helpOnly(opts.Args) { opts.Quiet = true } return opts }