github.com/sohaha/zlsgo@v1.7.13-0.20240501141223-10dd1a906f76/zcli/cli.go (about) 1 // Package zcli quickly build cli applications 2 package zcli 3 4 import ( 5 "bufio" 6 "flag" 7 "fmt" 8 "os" 9 "strings" 10 11 "github.com/sohaha/zlsgo/zlog" 12 "github.com/sohaha/zlsgo/zshell" 13 "github.com/sohaha/zlsgo/zstring" 14 "github.com/sohaha/zlsgo/ztype" 15 ) 16 17 func init() { 18 Log = zlog.New() 19 Log.ResetFlags(zlog.BitLevel) 20 // flag.CommandLine.SetOutput(ioutil.Discard) 21 flag.CommandLine.SetOutput(&errWrite{}) 22 flag.Usage = func() { 23 usage() 24 } 25 } 26 27 // Add registers a cmd for the provided subCommand name 28 func Add(name, description string, command Cmd) *cmdCont { 29 if name == "" { 30 Log.Error(GetLangText("command_empty")) 31 return &cmdCont{} 32 } 33 cmd := &cmdCont{ 34 name: name, 35 desc: description, 36 command: command, 37 requiredFlags: []string{}, 38 } 39 cmds[name] = cmd 40 cmdsKey = append(cmdsKey, name) 41 return cmd 42 } 43 44 // SetUnknownCommand set unknown command handle 45 func SetUnknownCommand(fn func(_ string)) { 46 unknownCommandFn = fn 47 } 48 49 func usage() { 50 showHeadr() 51 showFlagsAndRequired := func() { 52 if numOfGlobalFlags() > 0 { 53 showFlags(flag.CommandLine) 54 showRequired(flag.CommandLine, requiredFlags) 55 } 56 } 57 if len(cmds) == 0 { 58 Log.Printf("usage of %s\n\n", showText(FirstParameter)) 59 showFlagsAndRequired() 60 return 61 } 62 Log.Printf("usage: %s <command>\n\n\n", FirstParameter) 63 Log.Println("where <command> is one of:") 64 for _, name := range cmdsKey { 65 if cont, ok := cmds[name]; ok { 66 // for name, cont := range cmds { 67 Log.Printf(" "+tipText("%-19s")+" %s\n", name, cont.desc) 68 } 69 } 70 71 showFlagsAndRequired() 72 if !HidePrompt { 73 Log.Printf(showText("\nMore Command information, please use: %s <command> --help\n"), FirstParameter) 74 } 75 } 76 77 func showFlags(fg *flag.FlagSet) { 78 Log.Printf("\noptional flags:\n") 79 max := 40 80 showFlagsHelp() 81 flagsItems := zstring.Buffer() 82 fg.VisitAll(func(f *flag.Flag) { 83 s := zstring.Buffer() 84 flagsTitle := strings.Replace(f.Name, cliPrefix, "", 1) 85 for _, key := range varShortsKey { 86 if flagsTitle == key { 87 return 88 } 89 } 90 output := false 91 if flagsTitle == "version" { 92 output = true 93 } 94 name, usage := flag.UnquoteUsage(f) 95 for key, v := range varsKey { 96 shorts := v.shorts 97 if key == flagsTitle && len(shorts) > 0 { 98 for key := range shorts { 99 shorts[key] = "-" + shorts[key] 100 } 101 flagsTitle += ", " + strings.Join(shorts, ", ") 102 } 103 } 104 // if name == "" { 105 // name = "bool" 106 // } 107 sf := " -%-12s" 108 if len(name) > 0 { 109 newName := showText("<" + name + ">") 110 namePadLen := 12 + len(newName) - len(name) 111 flagsTitle += " " + newName 112 sf = " -%-" + ztype.ToString(namePadLen) + "s" 113 } 114 s.WriteString(warnText(fmt.Sprintf(sf, flagsTitle))) 115 if zstring.Len(s.String()) <= max { 116 s.WriteString("\t") 117 } else { 118 s.WriteString("\n \t") 119 } 120 s.WriteString(strings.Replace(usage, "\n", "\n \t", -1)) 121 defValue := ztype.ToString(f.DefValue) 122 if defValue != "" && defValue != "0" && defValue != "false" { 123 s.WriteString(fmt.Sprintf(" (default %v)", defValue)) 124 } 125 if output { 126 Log.Println(s.String()) 127 } else { 128 s.WriteString("\n") 129 flagsItems.WriteString(s.String()) 130 } 131 }) 132 133 Log.Println(flagsItems.String()) 134 } 135 136 // Start app 137 func Start(runFunc ...runFunc) { 138 if *flagDetach { 139 err := zshell.BgRun(strings.Join(runCmd, " ")) 140 if err != nil { 141 Error(err.Error()) 142 } 143 return 144 } 145 if matchingCmd != nil { 146 if *flagHelp { 147 showSubcommandUsage(flag.CommandLine, matchingCmd) 148 } else { 149 matchingCmd.command.Run(args) 150 } 151 return 152 } 153 requiredErr := parseRequiredFlags(flag.CommandLine, requiredFlags) 154 if requiredErr != nil { 155 Error(requiredErr.Error()) 156 } 157 158 isRunFunc := len(runFunc) > 0 159 if isRunFunc { 160 runFunc[0]() 161 } else { 162 Help() 163 } 164 } 165 166 // Run runnable 167 func Run(runFunc ...runFunc) (ok bool) { 168 isRunFunc := len(runFunc) > 0 169 parse(!isRunFunc) 170 Start(runFunc...) 171 return 172 } 173 174 func Input(problem string, required bool) (text string) { 175 if problem != "" { 176 fmt.Print(problem) 177 } 178 reader := bufio.NewReader(os.Stdin) 179 text, _ = reader.ReadString('\n') 180 if required && zstring.TrimSpace(text) == "" { 181 return Input(problem, required) 182 } 183 return 184 } 185 186 func Inputln(problem string, required bool) (text string) { 187 if problem != "" { 188 problem = problem + "\n" 189 } 190 return Input(problem, required) 191 } 192 193 func Current() (interface{}, bool) { 194 if matchingCmd == nil { 195 return nil, false 196 } 197 return matchingCmd.command, true 198 }