Files
gf/os/gcmd/gcmd_parser.go
hailaz ee24da4e72 refactor: interface{} to any and reflect.Ptr to reflect.Pointer (#4395)
This pull request standardizes the use of the Go 1.18+ `any` type alias
instead of `interface{}` throughout the codebase. The change improves
code readability and aligns with modern Go best practices. The update
touches many files, including core data structures, code generation
templates, logging utilities, and test data, ensuring consistency across
all usages.

**Type alias migration to `any`:**

* Replaced all instances of `interface{}` with `any` in core data
structures such as `garray` and in generated model structs (e.g.,
`TableUser`, `User1`, `User2`) to modernize type usage.
[[1]](diffhunk://#diff-3a1259e160a4dfa5fe49dfe739fbdb986c0d0a2220a709882ea48d3ae1b8f911L31-R31)
[[2]](diffhunk://#diff-6c19859cb32c7516ea95ddc8f8235460818eb2f24d2204308e0d9e1b19e7d90fL15-R19)
[[3]](diffhunk://#diff-a15ba2f5e830b4833c47b902515a4f9e5a4f83a3707698f3229b307ec3776b41L15-R18)
[[4]](diffhunk://#diff-52e0837e84d49221d1b810d88fdf78221f36cffcd664fb42f8aba49a79b974dcL15-R19)
[[5]](diffhunk://#diff-11c3457d1a23a4ca6ecd00d6b856289774936b6a708384cf03aff164044e7546L15-R19)
[[6]](diffhunk://#diff-2cff9cf8e6a0cc34087326d8c8149c3bbaf74c76fdbdf5a73daed13cc04249e1L15-R19)
* Updated function signatures, method parameters, and return types from
`interface{}` to `any` in various parts of the codebase, including code
generation, service logic, and logging utilities (e.g., `mlog`).
[[1]](diffhunk://#diff-175edfeea54490b8fe4e18ffcbea5835efaf8f0b8acf623359073987cae7eb76L48-R55)
[[2]](diffhunk://#diff-2b1953fb78cf3593d8c2c7d911e95b65fd0b847c30ed0b4d167d16fe6d781235L54-R74)
[[3]](diffhunk://#diff-e001b7a4b63603b9b14f00de78a4d570bb76c5f57d856a24643f071032e12356L66-R73)
[[4]](diffhunk://#diff-5582954e8a9983988dc8854ad82067fb2ac6269b988e07357ad8db1dfec5f1a0L39-R41)
[[5]](diffhunk://#diff-c5d51d56f487779a2b6207c7ad26c7a20bbadcc846ce094fe60ab4cabff58c51L107-R107)
[[6]](diffhunk://#diff-f96e6a9fdb416eb1804ceaba1fe0ac637bff22c43837f8bb849c2366ce72d4a1L116-R121)
[[7]](diffhunk://#diff-f94c83a1b08ae060d9346f4a6031fc4a7b9a0b894e02d9afaa09018b6598eac0L112-R112)
[[8]](diffhunk://#diff-748b11dbe8828dd4c040ec23cae0b8fe57ecf0a2d1b7694ea39102294e633c64L36-R36)
[[9]](diffhunk://#diff-748b11dbe8828dd4c040ec23cae0b8fe57ecf0a2d1b7694ea39102294e633c64L74-R74)
[[10]](diffhunk://#diff-748b11dbe8828dd4c040ec23cae0b8fe57ecf0a2d1b7694ea39102294e633c64L96-R96)

**Generated code and templates:**

* Adjusted generated files and code generation templates to output `any`
instead of `interface{}` for relevant struct fields and function
signatures, ensuring that new code generation aligns with the updated
convention.
[[1]](diffhunk://#diff-6c19859cb32c7516ea95ddc8f8235460818eb2f24d2204308e0d9e1b19e7d90fL15-R19)
[[2]](diffhunk://#diff-a15ba2f5e830b4833c47b902515a4f9e5a4f83a3707698f3229b307ec3776b41L15-R18)
[[3]](diffhunk://#diff-52e0837e84d49221d1b810d88fdf78221f36cffcd664fb42f8aba49a79b974dcL15-R19)
[[4]](diffhunk://#diff-11c3457d1a23a4ca6ecd00d6b856289774936b6a708384cf03aff164044e7546L15-R19)
[[5]](diffhunk://#diff-2cff9cf8e6a0cc34087326d8c8149c3bbaf74c76fdbdf5a73daed13cc04249e1L15-R19)
[[6]](diffhunk://#diff-175edfeea54490b8fe4e18ffcbea5835efaf8f0b8acf623359073987cae7eb76L48-R55)
[[7]](diffhunk://#diff-e001b7a4b63603b9b14f00de78a4d570bb76c5f57d856a24643f071032e12356L66-R73)
[[8]](diffhunk://#diff-5582954e8a9983988dc8854ad82067fb2ac6269b988e07357ad8db1dfec5f1a0L39-R41)

**Container and utility updates:**

* Refactored the `garray` container implementation and related
constructors/methods to use `[]any` instead of `[]interface{}`, along
with corresponding function signatures.
[[1]](diffhunk://#diff-3a1259e160a4dfa5fe49dfe739fbdb986c0d0a2220a709882ea48d3ae1b8f911L31-R31)
[[2]](diffhunk://#diff-3a1259e160a4dfa5fe49dfe739fbdb986c0d0a2220a709882ea48d3ae1b8f911L52-R52)
[[3]](diffhunk://#diff-3a1259e160a4dfa5fe49dfe739fbdb986c0d0a2220a709882ea48d3ae1b8f911L62-R62)
[[4]](diffhunk://#diff-3a1259e160a4dfa5fe49dfe739fbdb986c0d0a2220a709882ea48d3ae1b8f911L73-R86)
[[5]](diffhunk://#diff-3a1259e160a4dfa5fe49dfe739fbdb986c0d0a2220a709882ea48d3ae1b8f911L96-R97)
[[6]](diffhunk://#diff-3a1259e160a4dfa5fe49dfe739fbdb986c0d0a2220a709882ea48d3ae1b8f911L107-R114)
[[7]](diffhunk://#diff-3a1259e160a4dfa5fe49dfe739fbdb986c0d0a2220a709882ea48d3ae1b8f911L124-R124)
[[8]](diffhunk://#diff-3a1259e160a4dfa5fe49dfe739fbdb986c0d0a2220a709882ea48d3ae1b8f911L135-R143)
[[9]](diffhunk://#diff-3a1259e160a4dfa5fe49dfe739fbdb986c0d0a2220a709882ea48d3ae1b8f911L167-R167)

These changes collectively modernize the codebase and prepare it for
future Go developments by using the idiomatic `any` type.
2025-08-28 16:53:19 +08:00

267 lines
7.3 KiB
Go

// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://github.com/gogf/gf.
//
package gcmd
import (
"context"
"os"
"strings"
"github.com/gogf/gf/v2/container/gvar"
"github.com/gogf/gf/v2/errors/gcode"
"github.com/gogf/gf/v2/errors/gerror"
"github.com/gogf/gf/v2/internal/command"
"github.com/gogf/gf/v2/internal/json"
"github.com/gogf/gf/v2/text/gregex"
"github.com/gogf/gf/v2/text/gstr"
)
// ParserOption manages the parsing options.
type ParserOption struct {
CaseSensitive bool // Marks options parsing in case-sensitive way.
Strict bool // Whether stops parsing and returns error if invalid option passed.
}
// Parser for arguments.
type Parser struct {
option ParserOption // Parse option.
parsedArgs []string // As name described.
parsedOptions map[string]string // As name described.
passedOptions map[string]bool // User passed supported options, like: map[string]bool{"name,n":true}
supportedOptions map[string]bool // Option [OptionName:WhetherNeedArgument], like: map[string]bool{"name":true, "n":true}
commandFuncMap map[string]func() // Command function map for function handler.
}
// ParserFromCtx retrieves and returns Parser from context.
func ParserFromCtx(ctx context.Context) *Parser {
if v := ctx.Value(CtxKeyParser); v != nil {
if p, ok := v.(*Parser); ok {
return p
}
}
return nil
}
// Parse creates and returns a new Parser with os.Args and supported options.
//
// Note that the parameter `supportedOptions` is as [option name: need argument], which means
// the value item of `supportedOptions` indicates whether corresponding option name needs argument or not.
//
// The optional parameter `strict` specifies whether stops parsing and returns error if invalid option passed.
func Parse(supportedOptions map[string]bool, option ...ParserOption) (*Parser, error) {
if supportedOptions == nil {
command.Init(os.Args...)
return &Parser{
parsedArgs: GetArgAll(),
parsedOptions: GetOptAll(),
}, nil
}
return ParseArgs(os.Args, supportedOptions, option...)
}
// ParseArgs creates and returns a new Parser with given arguments and supported options.
//
// Note that the parameter `supportedOptions` is as [option name: need argument], which means
// the value item of `supportedOptions` indicates whether corresponding option name needs argument or not.
//
// The optional parameter `strict` specifies whether stops parsing and returns error if invalid option passed.
func ParseArgs(args []string, supportedOptions map[string]bool, option ...ParserOption) (*Parser, error) {
if supportedOptions == nil {
command.Init(args...)
return &Parser{
parsedArgs: GetArgAll(),
parsedOptions: GetOptAll(),
}, nil
}
var parserOption ParserOption
if len(option) > 0 {
parserOption = option[0]
}
parser := &Parser{
option: parserOption,
parsedArgs: make([]string, 0),
parsedOptions: make(map[string]string),
passedOptions: supportedOptions,
supportedOptions: make(map[string]bool),
commandFuncMap: make(map[string]func()),
}
for name, needArgument := range supportedOptions {
for _, v := range strings.Split(name, ",") {
parser.supportedOptions[strings.TrimSpace(v)] = needArgument
}
}
for i := 0; i < len(args); {
if option := parser.parseOption(args[i]); option != "" {
array, _ := gregex.MatchString(`^(.+?)=(.+)$`, option)
if len(array) == 3 {
if parser.isOptionValid(array[1]) {
parser.setOptionValue(array[1], array[2])
}
} else {
if parser.isOptionValid(option) {
if parser.isOptionNeedArgument(option) {
if i < len(args)-1 {
parser.setOptionValue(option, args[i+1])
i += 2
continue
}
} else {
parser.setOptionValue(option, "")
i++
continue
}
} else {
// Multiple options?
if array = parser.parseMultiOption(option); len(array) > 0 {
for _, v := range array {
parser.setOptionValue(v, "")
}
i++
continue
} else if parser.option.Strict {
return nil, gerror.NewCodef(gcode.CodeInvalidParameter, `invalid option '%s'`, args[i])
}
}
}
} else {
parser.parsedArgs = append(parser.parsedArgs, args[i])
}
i++
}
return parser, nil
}
// parseMultiOption parses option to multiple valid options like: --dav.
// It returns nil if given option is not multi-option.
func (p *Parser) parseMultiOption(option string) []string {
for i := 1; i <= len(option); i++ {
s := option[:i]
if p.isOptionValid(s) && !p.isOptionNeedArgument(s) {
if i == len(option) {
return []string{s}
}
array := p.parseMultiOption(option[i:])
if len(array) == 0 {
return nil
}
return append(array, s)
}
}
return nil
}
func (p *Parser) parseOption(argument string) string {
array, _ := gregex.MatchString(`^\-{1,2}(.+)$`, argument)
if len(array) == 2 {
return array[1]
}
return ""
}
func (p *Parser) isOptionValid(name string) bool {
// Case-Sensitive.
if p.option.CaseSensitive {
_, ok := p.supportedOptions[name]
return ok
}
// Case-InSensitive.
for optionName := range p.supportedOptions {
if gstr.Equal(optionName, name) {
return true
}
}
return false
}
func (p *Parser) isOptionNeedArgument(name string) bool {
return p.supportedOptions[name]
}
// setOptionValue sets the option value for name and according alias.
func (p *Parser) setOptionValue(name, value string) {
// Accurate option name match.
for optionName := range p.passedOptions {
optionNameAndShort := gstr.SplitAndTrim(optionName, ",")
for _, optionNameItem := range optionNameAndShort {
if optionNameItem == name {
for _, v := range optionNameAndShort {
p.parsedOptions[v] = value
}
return
}
}
}
// Fuzzy option name match.
for optionName := range p.passedOptions {
optionNameAndShort := gstr.SplitAndTrim(optionName, ",")
for _, optionNameItem := range optionNameAndShort {
if strings.EqualFold(optionNameItem, name) {
for _, v := range optionNameAndShort {
p.parsedOptions[v] = value
}
return
}
}
}
}
// GetOpt returns the option value named `name` as gvar.Var.
func (p *Parser) GetOpt(name string, def ...any) *gvar.Var {
if p == nil {
return nil
}
if v, ok := p.parsedOptions[name]; ok {
return gvar.New(v)
}
if len(def) > 0 {
return gvar.New(def[0])
}
return nil
}
// GetOptAll returns all parsed options.
func (p *Parser) GetOptAll() map[string]string {
if p == nil {
return nil
}
return p.parsedOptions
}
// GetArg returns the argument at `index` as gvar.Var.
func (p *Parser) GetArg(index int, def ...string) *gvar.Var {
if p == nil {
return nil
}
if index >= 0 && index < len(p.parsedArgs) {
return gvar.New(p.parsedArgs[index])
}
if len(def) > 0 {
return gvar.New(def[0])
}
return nil
}
// GetArgAll returns all parsed arguments.
func (p *Parser) GetArgAll() []string {
if p == nil {
return nil
}
return p.parsedArgs
}
// MarshalJSON implements the interface MarshalJSON for json.Marshal.
func (p *Parser) MarshalJSON() ([]byte, error) {
return json.Marshal(map[string]any{
"parsedArgs": p.parsedArgs,
"parsedOptions": p.parsedOptions,
"passedOptions": p.passedOptions,
"supportedOptions": p.supportedOptions,
})
}