github.com/sohaha/zlsgo@v1.7.13-0.20240501141223-10dd1a906f76/zcli/parse.go (about)

     1  package zcli
     2  
     3  import (
     4  	"flag"
     5  	"fmt"
     6  	"os"
     7  	"strings"
     8  	"sync"
     9  
    10  	"github.com/sohaha/zlsgo/zarray"
    11  	"github.com/sohaha/zlsgo/ztype"
    12  )
    13  
    14  var runCmd = []string{os.Args[0]}
    15  
    16  func parse(outHelp bool) {
    17  	parseCommand(outHelp)
    18  	parseSubcommand(flag.Args())
    19  }
    20  
    21  func parseRequiredFlags(fs *flag.FlagSet, requiredFlags []string) (err error) {
    22  	requiredFlagsLen := len(requiredFlags)
    23  	if requiredFlagsLen > 0 {
    24  		flagMap := zarray.NewArray(requiredFlagsLen)
    25  		for _, flagName := range requiredFlags {
    26  			flagMap.Push(flagName)
    27  		}
    28  		fs.Visit(func(f *flag.Flag) {
    29  			Log.Error(f.Name)
    30  
    31  			_, _ = flagMap.RemoveValue(f.Name)
    32  		})
    33  		flagMapLen := flagMap.Length()
    34  		if flagMapLen > 0 && !*flagHelp {
    35  			arr := make([]string, flagMapLen)
    36  			for i := 0; i < flagMapLen; i++ {
    37  				value, _ := flagMap.Get(i)
    38  				arr[i] = "-" + ztype.ToString(value)
    39  			}
    40  			err = fmt.Errorf("required flags: %s", strings.Join(arr, ", "))
    41  		}
    42  	}
    43  	return
    44  }
    45  
    46  var parseDone sync.Once
    47  
    48  // Parse Parse command line arguments
    49  func Parse(arg ...[]string) (hasflag bool) {
    50  	parseDone.Do(func() {
    51  		if Version != "" {
    52  			flagVersion = SetVar("version", GetLangText("version")).short("V").Bool()
    53  		}
    54  		if EnableDetach {
    55  			flagDetach = SetVar("detach", GetLangText("detach")).Bool()
    56  		}
    57  	})
    58  
    59  	var argsData []string
    60  	if len(arg) == 1 {
    61  		argsData = arg[0]
    62  	} else {
    63  		argsData = os.Args[1:]
    64  	}
    65  	for k := range argsData {
    66  		s := argsData[k]
    67  		if !isDetach(s) {
    68  			runCmd = append(runCmd, s)
    69  		}
    70  		if len(s) < 2 || s[0] != '-' {
    71  			continue
    72  		}
    73  		prefix := "-"
    74  		if s[1] == '-' {
    75  			s = s[2:]
    76  			prefix = "--"
    77  		} else {
    78  			s = s[1:]
    79  		}
    80  		for key := range varsKey {
    81  			if key == s {
    82  				argsData[k] = prefix + cliPrefix + s
    83  			}
    84  		}
    85  	}
    86  
    87  	hasflag = len(argsData) > 0
    88  
    89  	if hasflag && argsData[0][0] != '-' {
    90  		parseSubcommand(argsData)
    91  		if matchingCmd != nil {
    92  			matchingCmd.command.Run(args)
    93  		}
    94  		osExit(0)
    95  	}
    96  
    97  	_ = flag.CommandLine.Parse(argsData)
    98  	v, ok := ShortValues["V"].(*bool)
    99  	if ok && *v {
   100  		*flagVersion = *v
   101  	}
   102  	if d, ok := ShortValues["D"].(*bool); ok && *d {
   103  		*flagDetach = *d
   104  	}
   105  	if *flagVersion {
   106  		showVersionNum(*v)
   107  		osExit(0)
   108  		return
   109  	}
   110  	return
   111  }
   112  
   113  func parseCommand(outHelp bool) {
   114  	Parse()
   115  	if len(cmds) < 1 {
   116  		return
   117  	}
   118  	flag.Usage = usage
   119  	requiredErr := parseRequiredFlags(flag.CommandLine, requiredFlags)
   120  	if requiredErr != nil {
   121  		if len(flag.Args()) > 0 {
   122  			Error(requiredErr.Error())
   123  		} else if outHelp {
   124  			Help()
   125  		}
   126  	}
   127  
   128  	if flag.NArg() < 1 {
   129  		if outHelp {
   130  			Help()
   131  		}
   132  		return
   133  	}
   134  }
   135  
   136  func parseSubcommand(Args []string) {
   137  	var name = ""
   138  	if len(Args) > 0 {
   139  		name = Args[0]
   140  	}
   141  	if cont, ok := cmds[name]; ok {
   142  		matchingCmd = cont
   143  		FirstParameter += " " + name
   144  		fsArgs := Args[1:]
   145  		fs := flag.NewFlagSet(name, flag.ExitOnError)
   146  		flag.CommandLine.VisitAll(func(f *flag.Flag) {
   147  			fs.Var(f.Value, f.Name, f.Usage)
   148  		})
   149  		flag.CommandLine = fs
   150  		subcommand := &Subcommand{
   151  			Name:        cont.name,
   152  			Desc:        cont.desc,
   153  			Supplement:  cont.Supplement,
   154  			Parameter:   FirstParameter,
   155  			CommandLine: fs,
   156  		}
   157  		cont.command.Flags(subcommand)
   158  		fs.SetOutput(&errWrite{})
   159  		fs.Usage = func() {
   160  			Log.Printf("%s\n\n", subcommand.Desc)
   161  			if subcommand.Supplement != "" {
   162  				Log.Printf("%s\n\n", subcommand.Supplement)
   163  			}
   164  			Log.Printf("\nusage of %s\n\n", subcommand.Parameter)
   165  			showFlags(fs)
   166  			showRequired(fs, cont.requiredFlags)
   167  		}
   168  		_ = fs.Parse(fsArgs)
   169  		args = fs.Args()
   170  		argsIsHelp(args)
   171  		flagMap := zarray.NewArray(len(cont.requiredFlags))
   172  		for _, flagName := range cont.requiredFlags {
   173  			flagMap.Push(flagName)
   174  		}
   175  		fs.Visit(func(f *flag.Flag) {
   176  			_, _ = flagMap.RemoveValue(f.Name)
   177  		})
   178  		flagMapLen := flagMap.Length()
   179  		if flagMapLen > 0 && !*flagHelp {
   180  			arr := make([]string, flagMapLen)
   181  			for i := 0; i < flagMapLen; i++ {
   182  				value, _ := flagMap.Get(i)
   183  				arr[i] = "-" + ztype.ToString(value)
   184  			}
   185  			Error("required flags: %s", strings.Join(arr, ", "))
   186  		}
   187  	} else if name != "" {
   188  		unknownCommandFn(name)
   189  		osExit(1)
   190  	}
   191  }