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  }