github.com/gogf/gf@v1.16.9/os/gcmd/gcmd_parser.go (about) 1 // Copyright GoFrame Author(https://goframe.org). All Rights Reserved. 2 // 3 // This Source Code Form is subject to the terms of the MIT License. 4 // If a copy of the MIT was not distributed with this file, 5 // You can obtain one at https://github.com/gogf/gf. 6 // 7 8 package gcmd 9 10 import ( 11 "github.com/gogf/gf/errors/gcode" 12 "github.com/gogf/gf/errors/gerror" 13 "github.com/gogf/gf/internal/json" 14 "os" 15 "strings" 16 17 "github.com/gogf/gf/text/gstr" 18 19 "github.com/gogf/gf/container/gvar" 20 21 "github.com/gogf/gf/text/gregex" 22 ) 23 24 // Parser for arguments. 25 type Parser struct { 26 strict bool // Whether stops parsing and returns error if invalid option passed. 27 parsedArgs []string // As name described. 28 parsedOptions map[string]string // As name described. 29 passedOptions map[string]bool // User passed supported options. 30 supportedOptions map[string]bool // Option [option name : need argument]. 31 commandFuncMap map[string]func() // Command function map for function handler. 32 } 33 34 // Parse creates and returns a new Parser with os.Args and supported options. 35 // 36 // Note that the parameter <supportedOptions> is as [option name: need argument], which means 37 // the value item of <supportedOptions> indicates whether corresponding option name needs argument or not. 38 // 39 // The optional parameter <strict> specifies whether stops parsing and returns error if invalid option passed. 40 func Parse(supportedOptions map[string]bool, strict ...bool) (*Parser, error) { 41 return ParseWithArgs(os.Args, supportedOptions, strict...) 42 } 43 44 // ParseWithArgs creates and returns a new Parser with given arguments and supported options. 45 // 46 // Note that the parameter <supportedOptions> is as [option name: need argument], which means 47 // the value item of <supportedOptions> indicates whether corresponding option name needs argument or not. 48 // 49 // The optional parameter <strict> specifies whether stops parsing and returns error if invalid option passed. 50 func ParseWithArgs(args []string, supportedOptions map[string]bool, strict ...bool) (*Parser, error) { 51 strictParsing := false 52 if len(strict) > 0 { 53 strictParsing = strict[0] 54 } 55 parser := &Parser{ 56 strict: strictParsing, 57 parsedArgs: make([]string, 0), 58 parsedOptions: make(map[string]string), 59 passedOptions: supportedOptions, 60 supportedOptions: make(map[string]bool), 61 commandFuncMap: make(map[string]func()), 62 } 63 for name, needArgument := range supportedOptions { 64 for _, v := range strings.Split(name, ",") { 65 parser.supportedOptions[strings.TrimSpace(v)] = needArgument 66 } 67 } 68 69 for i := 0; i < len(args); { 70 if option := parser.parseOption(args[i]); option != "" { 71 array, _ := gregex.MatchString(`^(.+?)=(.+)$`, option) 72 if len(array) == 3 { 73 if parser.isOptionValid(array[1]) { 74 parser.setOptionValue(array[1], array[2]) 75 } 76 } else { 77 if parser.isOptionValid(option) { 78 if parser.isOptionNeedArgument(option) { 79 if i < len(args)-1 { 80 parser.setOptionValue(option, args[i+1]) 81 i += 2 82 continue 83 } 84 } else { 85 parser.setOptionValue(option, "") 86 i++ 87 continue 88 } 89 } else { 90 // Multiple options? 91 if array := parser.parseMultiOption(option); len(array) > 0 { 92 for _, v := range array { 93 parser.setOptionValue(v, "") 94 } 95 i++ 96 continue 97 } else if parser.strict { 98 return nil, gerror.NewCodef(gcode.CodeInvalidParameter, `invalid option '%s'`, args[i]) 99 } 100 } 101 } 102 } else { 103 parser.parsedArgs = append(parser.parsedArgs, args[i]) 104 } 105 i++ 106 } 107 return parser, nil 108 } 109 110 // parseMultiOption parses option to multiple valid options like: --dav. 111 // It returns nil if given option is not multi-option. 112 func (p *Parser) parseMultiOption(option string) []string { 113 for i := 1; i <= len(option); i++ { 114 s := option[:i] 115 if p.isOptionValid(s) && !p.isOptionNeedArgument(s) { 116 if i == len(option) { 117 return []string{s} 118 } 119 array := p.parseMultiOption(option[i:]) 120 if len(array) == 0 { 121 return nil 122 } 123 return append(array, s) 124 } 125 } 126 return nil 127 } 128 129 func (p *Parser) parseOption(argument string) string { 130 array, _ := gregex.MatchString(`^\-{1,2}(.+)$`, argument) 131 if len(array) == 2 { 132 return array[1] 133 } 134 return "" 135 } 136 137 func (p *Parser) isOptionValid(name string) bool { 138 _, ok := p.supportedOptions[name] 139 return ok 140 } 141 142 func (p *Parser) isOptionNeedArgument(name string) bool { 143 return p.supportedOptions[name] 144 } 145 146 // setOptionValue sets the option value for name and according alias. 147 func (p *Parser) setOptionValue(name, value string) { 148 for optionName, _ := range p.passedOptions { 149 array := gstr.SplitAndTrim(optionName, ",") 150 for _, v := range array { 151 if strings.EqualFold(v, name) { 152 for _, v := range array { 153 p.parsedOptions[v] = value 154 } 155 return 156 } 157 } 158 } 159 } 160 161 // GetOpt returns the option value named <name>. 162 func (p *Parser) GetOpt(name string, def ...string) string { 163 if v, ok := p.parsedOptions[name]; ok { 164 return v 165 } 166 if len(def) > 0 { 167 return def[0] 168 } 169 return "" 170 } 171 172 // GetOptVar returns the option value named <name> as gvar.Var. 173 func (p *Parser) GetOptVar(name string, def ...interface{}) *gvar.Var { 174 if p.ContainsOpt(name) { 175 return gvar.New(p.GetOpt(name)) 176 } 177 if len(def) > 0 { 178 return gvar.New(def[0]) 179 } 180 return gvar.New(nil) 181 } 182 183 // GetOptAll returns all parsed options. 184 func (p *Parser) GetOptAll() map[string]string { 185 return p.parsedOptions 186 } 187 188 // ContainsOpt checks whether option named <name> exist in the arguments. 189 func (p *Parser) ContainsOpt(name string) bool { 190 _, ok := p.parsedOptions[name] 191 return ok 192 } 193 194 // GetArg returns the argument at <index>. 195 func (p *Parser) GetArg(index int, def ...string) string { 196 if index < len(p.parsedArgs) { 197 return p.parsedArgs[index] 198 } 199 if len(def) > 0 { 200 return def[0] 201 } 202 return "" 203 } 204 205 // GetArgVar returns the argument at <index> as gvar.Var. 206 func (p *Parser) GetArgVar(index int, def ...string) *gvar.Var { 207 return gvar.New(p.GetArg(index, def...)) 208 } 209 210 // GetArgAll returns all parsed arguments. 211 func (p *Parser) GetArgAll() []string { 212 return p.parsedArgs 213 } 214 215 // MarshalJSON implements the interface MarshalJSON for json.Marshal. 216 func (p *Parser) MarshalJSON() ([]byte, error) { 217 return json.Marshal(map[string]interface{}{ 218 "parsedArgs": p.parsedArgs, 219 "parsedOptions": p.parsedOptions, 220 "passedOptions": p.passedOptions, 221 "supportedOptions": p.supportedOptions, 222 }) 223 }