gcmd_parser.go 6.8 KB

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