gcmd_parser.go 6.2 KB

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