github.com/sandwich-go/boost@v1.3.29/xcmd/cmd.go (about)

     1  package xcmd
     2  
     3  import (
     4  	"flag"
     5  	"fmt"
     6  	"os"
     7  	"regexp"
     8  	"strings"
     9  )
    10  
    11  var (
    12  	flagPrefix    = "xcmd_"
    13  	parsedOptions = make(map[string]string)
    14  	argumentRegex = regexp.MustCompile(`^\-{1,2}([\w\?\.\-]+)(=){0,1}(.*)$`)
    15  	formal        = make(map[string]string)
    16  	invalidChars  = ".-/\\"
    17  )
    18  
    19  const (
    20  	FlagPrefix              = "sandwich_"
    21  	DefaultStringFalse      = "false"
    22  	DefaultStringTrue       = "true"
    23  	DefaultStringEmpty      = ""
    24  	cmdEnvSandwichBuildInfo = "sandwich_build_info"
    25  	DefaultTopologyRender   = "true"
    26  )
    27  
    28  // SetFlagPrefix 设置 flag 的前缀
    29  func SetFlagPrefix(prefix string) { flagPrefix = prefix }
    30  
    31  // GetFlagPrefix 获取 flag 的前缀
    32  func GetFlagPrefix() string { return flagPrefix }
    33  
    34  func addFlagIfNotExists(fs *flag.FlagSet, name string, value string) {
    35  	if fs.Lookup(name) == nil {
    36  		usage := fmt.Sprintf("%sbase_command flag:%s default:%s", flagPrefix, name, value)
    37  		_ = fs.String(name, value, usage)
    38  	}
    39  }
    40  
    41  // DeclareInto 将 command 预留的 flag 定义添加到指定的 FlagSet 中
    42  func DeclareInto(fs *flag.FlagSet) {
    43  	for name, val := range formal {
    44  		addFlagIfNotExists(fs, name, val)
    45  	}
    46  }
    47  
    48  // AddFlag 添加框架内默认的 flag 数据
    49  // name 必须有 flagPrefix 前缀,不能包含.-/\字符
    50  func AddFlag(name, defaultVal string) error {
    51  	if !strings.HasPrefix(name, flagPrefix) {
    52  		return fmt.Errorf("command flag must has prefix: %s", flagPrefix)
    53  	}
    54  	if strings.ContainsAny(name, invalidChars) {
    55  		return fmt.Errorf("command flag should only use _ as world separator, format must be:%s<package name>_<variable name>", flagPrefix)
    56  	}
    57  	_, alreadyThere := formal[name]
    58  	if alreadyThere {
    59  		return fmt.Errorf("flag redefined: %s", name)
    60  	}
    61  	formal[name] = defaultVal
    62  	addFlagIfNotExists(flag.CommandLine, name, defaultVal)
    63  	return nil
    64  }
    65  
    66  // MustAddFlag 添加框架内默认的 flag 数据,若失败,则panic
    67  // name 必须有 flagPrefix 前缀,不能包含特殊的字符
    68  func MustAddFlag(name, defaultVal string) {
    69  	err := AddFlag(name, defaultVal)
    70  	if err != nil {
    71  		panic(err)
    72  	}
    73  }
    74  
    75  // Init 根据给定的参数初始化预留的参数,如果args为空,则会使用os.Args初始化
    76  func Init(args ...string) {
    77  	if len(args) == 0 {
    78  		if len(parsedOptions) != 0 { // 已经初始化过数据
    79  			return
    80  		}
    81  		args = os.Args
    82  	}
    83  
    84  	if len(parsedOptions) == 0 {
    85  		parsedOptions = make(map[string]string)
    86  	}
    87  	for i := 0; i < len(args); {
    88  		match := argumentRegex.FindStringSubmatch(args[i])
    89  		if len(match) > 2 {
    90  			if match[2] == "=" {
    91  				// -xcmd_debug=1
    92  				// array  4 [-xcmd_debug=1 xcmd_debug = 1]
    93  				parsedOptions[match[1]] = match[3]
    94  			} else if i < len(args)-1 {
    95  				if len(args[i+1]) > 0 && args[i+1][0] == '-' {
    96  					parsedOptions[match[1]] = match[3]
    97  				} else {
    98  					parsedOptions[match[1]] = args[i+1]
    99  					i += 2
   100  					continue
   101  				}
   102  			} else {
   103  				parsedOptions[match[1]] = match[3]
   104  			}
   105  		}
   106  		i++
   107  	}
   108  }
   109  
   110  // GetOptWithEnv 返回命令定义的行参数或Env中的参数
   111  // 1. 命令行参数,小写,单词之间以_分割,$FlagPrefix_<package name>_<variable name>
   112  // 2. 环境变量参数,小写(历史遗留),且单词之间以_分割,$FlagPrefix_<package name>_<variable name>
   113  func GetOptWithEnv(key string, def ...string) string {
   114  	Init()
   115  	if v, ok := parsedOptions[key]; ok {
   116  		return v
   117  	}
   118  	if r, ok := os.LookupEnv(key); ok {
   119  		return r
   120  	}
   121  	if len(def) > 0 {
   122  		return def[0]
   123  	}
   124  	if v, ok := formal[key]; ok {
   125  		return v
   126  	}
   127  	return ""
   128  }
   129  
   130  // ContainsOpt checks whether option named `name` exist in the arguments.
   131  func ContainsOpt(name string) bool {
   132  	Init()
   133  	_, ok := parsedOptions[name]
   134  	return ok
   135  }