expose package internal/structs as os/gstructs; add package gtag for custom tag storage feature

This commit is contained in:
John Guo
2021-11-24 16:17:50 +08:00
parent e2abee7ba4
commit 7b22355abb
27 changed files with 378 additions and 235 deletions

View File

@ -19,8 +19,8 @@ import (
"github.com/gogf/gf/v2/errors/gerror"
"github.com/gogf/gf/v2/internal/empty"
"github.com/gogf/gf/v2/internal/json"
"github.com/gogf/gf/v2/internal/structs"
"github.com/gogf/gf/v2/internal/utils"
"github.com/gogf/gf/v2/os/gstructs"
"github.com/gogf/gf/v2/os/gtime"
"github.com/gogf/gf/v2/text/gregex"
"github.com/gogf/gf/v2/text/gstr"
@ -96,7 +96,7 @@ func getTableNameFromOrmTag(object interface{}) string {
}
// Use the struct name of snake case.
if tableName == "" {
if t, err := structs.StructType(object); err != nil {
if t, err := gstructs.StructType(object); err != nil {
panic(err)
} else {
tableName = gstr.CaseSnakeFirstUpper(
@ -306,9 +306,9 @@ func doQuoteString(s, charLeft, charRight string) string {
func getFieldsFromStructOrMap(structOrMap interface{}) (fields []string) {
fields = []string{}
if utils.IsStruct(structOrMap) {
structFields, _ := structs.Fields(structs.FieldsInput{
structFields, _ := gstructs.Fields(gstructs.FieldsInput{
Pointer: structOrMap,
RecursiveOption: structs.RecursiveOptionEmbeddedNoTag,
RecursiveOption: gstructs.RecursiveOptionEmbeddedNoTag,
})
for _, structField := range structFields {
if tag := structField.Tag(OrmTagForStruct); tag != "" && gregex.IsMatchString(regularFieldNameRegPattern, tag) {

View File

@ -13,8 +13,8 @@ import (
"github.com/gogf/gf/v2/errors/gcode"
"github.com/gogf/gf/v2/errors/gerror"
"github.com/gogf/gf/v2/internal/structs"
"github.com/gogf/gf/v2/internal/utils"
"github.com/gogf/gf/v2/os/gstructs"
"github.com/gogf/gf/v2/text/gregex"
"github.com/gogf/gf/v2/text/gstr"
)
@ -65,10 +65,10 @@ func (m *Model) doWithScanStruct(pointer interface{}) error {
err error
allowedTypeStrArray = make([]string, 0)
)
currentStructFieldMap, err := structs.FieldMap(structs.FieldMapInput{
currentStructFieldMap, err := gstructs.FieldMap(gstructs.FieldMapInput{
Pointer: pointer,
PriorityTagArray: nil,
RecursiveOption: structs.RecursiveOptionEmbeddedNoTag,
RecursiveOption: gstructs.RecursiveOptionEmbeddedNoTag,
})
if err != nil {
return err
@ -77,7 +77,7 @@ func (m *Model) doWithScanStruct(pointer interface{}) error {
if !m.withAll {
for _, field := range currentStructFieldMap {
for _, withItem := range m.withArray {
withItemReflectValueType, err := structs.StructType(withItem)
withItemReflectValueType, err := gstructs.StructType(withItem)
if err != nil {
return err
}
@ -137,7 +137,7 @@ func (m *Model) doWithScanStruct(pointer interface{}) error {
}
// It automatically retrieves struct field names from current attribute struct/slice.
if structType, err := structs.StructType(field.Value); err != nil {
if structType, err := gstructs.StructType(field.Value); err != nil {
return err
} else {
fieldKeys = structType.FieldKeys()
@ -176,10 +176,10 @@ func (m *Model) doWithScanStructs(pointer interface{}) error {
err error
allowedTypeStrArray = make([]string, 0)
)
currentStructFieldMap, err := structs.FieldMap(structs.FieldMapInput{
currentStructFieldMap, err := gstructs.FieldMap(gstructs.FieldMapInput{
Pointer: pointer,
PriorityTagArray: nil,
RecursiveOption: structs.RecursiveOptionEmbeddedNoTag,
RecursiveOption: gstructs.RecursiveOptionEmbeddedNoTag,
})
if err != nil {
return err
@ -188,7 +188,7 @@ func (m *Model) doWithScanStructs(pointer interface{}) error {
if !m.withAll {
for _, field := range currentStructFieldMap {
for _, withItem := range m.withArray {
withItemReflectValueType, err := structs.StructType(withItem)
withItemReflectValueType, err := gstructs.StructType(withItem)
if err != nil {
return err
}
@ -244,7 +244,7 @@ func (m *Model) doWithScanStructs(pointer interface{}) error {
}
// It automatically retrieves struct field names from current attribute struct/slice.
if structType, err := structs.StructType(field.Value); err != nil {
if structType, err := gstructs.StructType(field.Value); err != nil {
return err
} else {
fieldKeys = structType.FieldKeys()
@ -281,7 +281,7 @@ type parseWithTagInFieldStructOutput struct {
Order string
}
func (m *Model) parseWithTagInFieldStruct(field structs.Field) (output parseWithTagInFieldStructOutput) {
func (m *Model) parseWithTagInFieldStruct(field gstructs.Field) (output parseWithTagInFieldStructOutput) {
var (
match []string
ormTag = field.Tag(OrmTagForStruct)

View File

@ -12,7 +12,7 @@ import (
"github.com/gogf/gf/v2/errors/gcode"
"github.com/gogf/gf/v2/errors/gerror"
"github.com/gogf/gf/v2/internal/structs"
"github.com/gogf/gf/v2/os/gstructs"
"github.com/gogf/gf/v2/text/gstr"
"github.com/gogf/gf/v2/util/gconv"
"github.com/gogf/gf/v2/util/gutil"
@ -356,9 +356,9 @@ func doScanList(in doScanListInput) (err error) {
if in.RelationFields != "" && !relationBindToFieldNameChecked {
relationFromAttrField = relationFromAttrValue.FieldByName(relationBindToFieldName)
if !relationFromAttrField.IsValid() {
filedMap, _ := structs.FieldMap(structs.FieldMapInput{
filedMap, _ := gstructs.FieldMap(gstructs.FieldMapInput{
Pointer: relationFromAttrValue,
RecursiveOption: structs.RecursiveOptionEmbeddedNoTag,
RecursiveOption: gstructs.RecursiveOptionEmbeddedNoTag,
})
if key, _ := gutil.MapPossibleItemByKey(gconv.Map(filedMap), relationBindToFieldName); key == "" {
return gerror.NewCodef(

View File

@ -1,26 +0,0 @@
// 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 structs provides functions for struct information retrieving and struct conversion.
//
// Inspired and improved from: https://github.com/fatih/structs
package structs
import (
"reflect"
)
// Type wraps reflect.Type for additional features.
type Type struct {
reflect.Type
}
// Field contains information of a struct field .
type Field struct {
Value reflect.Value // The underlying value of the field.
Field reflect.StructField // The underlying field of the field.
TagValue string // Retrieved tag value. There might be more than one tags in the field, but only one can be retrieved according to calling function rules.
}

View File

@ -9,7 +9,7 @@ package ghttp
import (
"github.com/gogf/gf/v2/container/gvar"
"github.com/gogf/gf/v2/internal/empty"
"github.com/gogf/gf/v2/internal/structs"
"github.com/gogf/gf/v2/os/gstructs"
"github.com/gogf/gf/v2/util/gconv"
"github.com/gogf/gf/v2/util/gutil"
)
@ -179,7 +179,7 @@ func (r *Request) doGetRequestStruct(pointer interface{}, mapping ...map[string]
// mergeDefaultStructValue merges the request parameters with default values from struct tag definition.
func (r *Request) mergeDefaultStructValue(data map[string]interface{}, pointer interface{}) error {
tagFields, err := structs.TagFields(pointer, defaultValueTags)
tagFields, err := gstructs.TagFields(pointer, defaultValueTags)
if err != nil {
return err
}

View File

@ -25,7 +25,7 @@ type Command struct {
FuncWithValue FuncWithValue // Custom function with output parameters that can interact with command caller.
HelpFunc Function // Custom help function
Examples string // Usage examples.
Additional string // Additional custom info about this command.
Additional string // Additional info about this command, which will be appended to the end of help info.
parent *Command // Parent command for internal usage.
commands []Command // Sub commands of this command.
}
@ -62,9 +62,6 @@ func (c *Command) AddCommand(commands ...Command) error {
if cmd.Name == "" {
return gerror.New("command name should not be empty")
}
if cmd.Func == nil && cmd.FuncWithValue == nil {
return gerror.New("command function should not be empty")
}
cmd.parent = c
c.commands = append(c.commands, cmd)
}
@ -77,11 +74,11 @@ func (c *Command) AddObject(objects ...interface{}) error {
commands []Command
)
for _, object := range objects {
tempCommand, err := NewFromObject(object)
rootCommand, err := NewFromObject(object)
if err != nil {
return err
}
commands = append(commands, *tempCommand)
commands = append(commands, rootCommand)
}
return c.AddCommand(commands...)
}

View File

@ -14,8 +14,8 @@ import (
"github.com/gogf/gf/v2/container/gset"
"github.com/gogf/gf/v2/errors/gcode"
"github.com/gogf/gf/v2/errors/gerror"
"github.com/gogf/gf/v2/internal/structs"
"github.com/gogf/gf/v2/internal/utils"
"github.com/gogf/gf/v2/os/gstructs"
"github.com/gogf/gf/v2/text/gstr"
"github.com/gogf/gf/v2/util/gconv"
"github.com/gogf/gf/v2/util/gmeta"
@ -24,9 +24,8 @@ import (
)
const (
tagNameDc = `dc`
tagNameAd = `ad`
tagNameRoot = `root`
tagNameDc = `dc`
tagNameAd = `ad`
)
var (
@ -35,51 +34,42 @@ var (
)
// NewFromObject creates and returns a root command object using given object.
func NewFromObject(object interface{}) (rootCmd *Command, err error) {
func NewFromObject(object interface{}) (rootCmd Command, err error) {
originValueAndKind := utils.OriginValueAndKind(object)
if originValueAndKind.OriginKind != reflect.Struct {
return nil, gerror.Newf(
err = gerror.Newf(
`input object should be type of struct, but got "%s"`,
originValueAndKind.InputValue.Type().String(),
)
return
}
// Root command creating.
rootCmd, err = newCommandFromObjectMeta(object)
if err != nil {
return
}
// Sub command creating.
var (
nameSet = gset.NewStrSet()
subCommands []Command
)
for i := 0; i < originValueAndKind.InputValue.NumMethod(); i++ {
var (
root bool
method = originValueAndKind.InputValue.Method(i)
methodCommand Command
)
methodCommand, root, err = newCommandFromMethod(object, method)
methodCommand, err = newCommandFromMethod(object, method)
if err != nil {
return nil, err
return
}
if nameSet.Contains(methodCommand.Name) {
return nil, gerror.Newf(
err = gerror.Newf(
`command name should be unique, found duplicated command name in method "%s"`,
method.Type().String(),
)
return
}
if root {
if rootCmd != nil {
return nil, gerror.Newf(
`there should be only one root command in object, found duplicated in method "%s"`,
method.Type().String(),
)
}
rootCmd = &methodCommand
} else {
subCommands = append(subCommands, methodCommand)
}
}
if rootCmd == nil {
return nil, gerror.Newf(
`there should be one root command in object when creating command from object, but found none in object "%s"`,
originValueAndKind.InputValue.Type().String(),
)
subCommands = append(subCommands, methodCommand)
}
if len(subCommands) > 0 {
err = rootCmd.AddCommand(subCommands...)
@ -87,7 +77,38 @@ func NewFromObject(object interface{}) (rootCmd *Command, err error) {
return
}
func newCommandFromMethod(object interface{}, method reflect.Value) (command Command, root bool, err error) {
func newCommandFromObjectMeta(object interface{}) (command Command, err error) {
var (
metaData = gmeta.Data(object)
)
if len(metaData) == 0 {
err = gerror.Newf(
`no meta data found in struct "%s"`,
reflect.TypeOf(object).String(),
)
return
}
if err = gconv.Scan(metaData, &command); err != nil {
return
}
// Name filed is necessary.
if command.Name == "" {
err = gerror.Newf(
`command name cannot be empty, "name" tag not found in meta of struct "%s"`,
reflect.TypeOf(object).String(),
)
return
}
if command.Description == "" {
command.Description = metaData[tagNameDc]
}
if command.Additional == "" {
command.Additional = metaData[tagNameAd]
}
return
}
func newCommandFromMethod(object interface{}, method reflect.Value) (command Command, err error) {
var (
reflectType = method.Type()
)
@ -153,28 +174,11 @@ func newCommandFromMethod(object interface{}, method reflect.Value) (command Com
}
// Command creating.
var (
metaData = gmeta.Data(inputObject.Interface())
)
if err = gconv.Scan(metaData, &command); err != nil {
if command, err = newCommandFromObjectMeta(inputObject.Interface()); err != nil {
return
}
root = gconv.Bool(metaData[tagNameRoot])
// Name filed is necessary.
if command.Name == "" {
err = gerror.Newf(
`command name cannot be empty, "name" tag not found in struct "%s"`,
inputObject.Type().String(),
)
return
}
if command.Description == "" {
command.Description = metaData[tagNameDc]
}
if command.Additional == "" {
command.Additional = metaData[tagNameAd]
}
// Options creating.
if command.Options, err = newOptionsFromInput(inputObject.Interface()); err != nil {
return
}
@ -237,7 +241,7 @@ func newCommandFromMethod(object interface{}, method reflect.Value) (command Com
// mergeDefaultStructValue merges the request parameters with default values from struct tag definition.
func mergeDefaultStructValue(data map[string]interface{}, pointer interface{}) error {
tagFields, err := structs.TagFields(pointer, defaultValueTags)
tagFields, err := gstructs.TagFields(pointer, defaultValueTags)
if err != nil {
return err
}
@ -262,11 +266,11 @@ func mergeDefaultStructValue(data map[string]interface{}, pointer interface{}) e
func newOptionsFromInput(object interface{}) (options []Option, err error) {
var (
fields []structs.Field
fields []gstructs.Field
)
fields, err = structs.Fields(structs.FieldsInput{
fields, err = gstructs.Fields(gstructs.FieldsInput{
Pointer: object,
RecursiveOption: structs.RecursiveOptionEmbeddedNoTag,
RecursiveOption: gstructs.RecursiveOptionEmbeddedNoTag,
})
for _, field := range fields {
var (

View File

@ -12,7 +12,6 @@ import (
"fmt"
"os"
"github.com/gogf/gf/v2/errors/gerror"
"github.com/gogf/gf/v2/text/gstr"
)
@ -31,6 +30,7 @@ func (c *Command) RunWithValue(ctx context.Context) (value interface{}, err erro
}
args := parser.GetArgAll()
if len(args) == 1 {
// If no arguments passed but binary name, it then prints help.
if c.HelpFunc != nil {
return nil, c.HelpFunc(ctx, parser)
}
@ -78,7 +78,11 @@ func (c *Command) doRun(ctx context.Context, parser *Parser) (value interface{},
if c.FuncWithValue != nil {
return c.FuncWithValue(ctx, parser)
}
return nil, gerror.New(`no function registered for current command`)
// If no function defined in current command, it then prints help.
if c.HelpFunc != nil {
return nil, c.HelpFunc(ctx, parser)
}
return nil, c.defaultHelpFunc(ctx, parser)
}
// reParse re-parses the arguments using option configuration of current command.

View File

@ -19,12 +19,9 @@ import (
"github.com/gogf/gf/v2/test/gtest"
)
type TestCmdObject struct{}
type TestCmdObjectInput struct {
g.Meta `root:"true" name:"root" usage:"root env/test" brief:"root env command" dc:"description" ad:"ad"`
type TestCmdObject struct {
g.Meta `name:"root" usage:"root env/test" brief:"root env command" dc:"description" ad:"ad"`
}
type TestCmdObjectOutput struct{}
type TestCmdObjectEnvInput struct {
g.Meta `name:"env" usage:"root env" brief:"root env command" dc:"root env command description" ad:"root env command ad"`
@ -39,10 +36,6 @@ type TestCmdObjectTestOutput struct {
Content string
}
func (TestCmdObject) Root(ctx context.Context, in TestCmdObjectInput) (out *TestCmdObjectOutput, err error) {
return
}
func (TestCmdObject) Env(ctx context.Context, in TestCmdObjectEnvInput) (out *TestCmdObjectEnvOutput, err error) {
return
}
@ -54,7 +47,23 @@ func (TestCmdObject) Test(ctx context.Context, in TestCmdObjectTestInput) (out *
return
}
func Test_Command_NewFromObject(t *testing.T) {
func Test_Command_NewFromObject_Help(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
var (
ctx = gctx.New()
cmd, err = gcmd.NewFromObject(&TestCmdObject{})
)
t.AssertNil(err)
t.Assert(cmd.Name, "root")
os.Args = []string{"root"}
value, err := cmd.RunWithValue(ctx)
t.AssertNil(err)
t.Assert(value, nil)
})
}
func Test_Command_NewFromObject_RunWithValue(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
var (
ctx = gctx.New()

54
os/gstructs/gstructs.go Normal file
View File

@ -0,0 +1,54 @@
// 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 gstructs provides functions for struct information retrieving.
package gstructs
import (
"reflect"
)
// Type wraps reflect.Type for additional features.
type Type struct {
reflect.Type
}
// Field contains information of a struct field .
type Field struct {
Value reflect.Value // The underlying value of the field.
Field reflect.StructField // The underlying field of the field.
TagValue string // Retrieved tag value. There might be more than one tags in the field, but only one can be retrieved according to calling function rules.
}
// FieldsInput is the input parameter struct type for function Fields.
type FieldsInput struct {
// Pointer should be type of struct/*struct.
Pointer interface{}
// RecursiveOption specifies the way retrieving the fields recursively if the attribute
// is an embedded struct. It is RecursiveOptionNone in default.
RecursiveOption int
}
// FieldMapInput is the input parameter struct type for function FieldMap.
type FieldMapInput struct {
// Pointer should be type of struct/*struct.
Pointer interface{}
// PriorityTagArray specifies the priority tag array for retrieving from high to low.
// If it's given `nil`, it returns map[name]Field, of which the `name` is attribute name.
PriorityTagArray []string
// RecursiveOption specifies the way retrieving the fields recursively if the attribute
// is an embedded struct. It is RecursiveOptionNone in default.
RecursiveOption int
}
const (
RecursiveOptionNone = 0 // No recursively retrieving fields as map if the field is an embedded struct.
RecursiveOptionEmbedded = 1 // Recursively retrieving fields as map if the field is an embedded struct.
RecursiveOptionEmbeddedNoTag = 2 // Recursively retrieving fields as map if the field is an embedded struct and the field has no tag.
)

View File

@ -4,12 +4,14 @@
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://github.com/gogf/gf.
package structs
package gstructs
import (
"reflect"
"regexp"
"strings"
"github.com/gogf/gf/v2/util/gtag"
)
const (
@ -17,13 +19,17 @@ const (
)
var (
tagMapRegex, _ = regexp.Compile(`([\w\-]+):"(.+?)"`)
tagMapRegex = regexp.MustCompile(`([\w\-]+):"(.+?)"`)
)
// Tag returns the value associated with key in the tag string. If there is no
// such key in the tag, Tag returns the empty string.
func (f *Field) Tag(key string) string {
return f.Field.Tag.Get(key)
s := f.Field.Tag.Get(key)
if s != "" {
s = gtag.Parse(s)
}
return s
}
// TagJsonName returns the `json` tag name string of the field.
@ -41,7 +47,11 @@ func (f *Field) TagJsonName() string {
// the tag string. If the tag does not have the conventional format,
// the value returned by Lookup is unspecified.
func (f *Field) TagLookup(key string) (value string, ok bool) {
return f.Field.Tag.Lookup(key)
value, ok = f.Field.Tag.Lookup(key)
if ok && value != "" {
value = gtag.Parse(value)
}
return
}
// IsEmbedded returns true if the given field is an anonymous field (embedded)
@ -62,7 +72,7 @@ func (f *Field) TagMap() map[string]string {
)
for _, m := range match {
if len(m) == 3 {
data[m[1]] = m[2]
data[m[1]] = gtag.Parse(m[2])
}
}
return data
@ -73,12 +83,13 @@ func (f *Field) IsExported() bool {
return f.Field.PkgPath == ""
}
// Name returns the name of the given field
// Name returns the name of the given field.
func (f *Field) Name() string {
return f.Field.Name
}
// Type returns the type of the given field
// Type returns the type of the given field.
// Note that this Type is not reflect.Type. If you need reflect.Type, please use Field.Type().Type.
func (f *Field) Type() Type {
return Type{
Type: f.Field.Type,
@ -103,34 +114,6 @@ func (f *Field) OriginalKind() reflect.Kind {
return kind
}
const (
RecursiveOptionNone = 0 // No recursively retrieving fields as map if the field is an embedded struct.
RecursiveOptionEmbedded = 1 // Recursively retrieving fields as map if the field is an embedded struct.
RecursiveOptionEmbeddedNoTag = 2 // Recursively retrieving fields as map if the field is an embedded struct and the field has no tag.
)
type FieldsInput struct {
// Pointer should be type of struct/*struct.
Pointer interface{}
// RecursiveOption specifies the way retrieving the fields recursively if the attribute
// is an embedded struct. It is RecursiveOptionNone in default.
RecursiveOption int
}
type FieldMapInput struct {
// Pointer should be type of struct/*struct.
Pointer interface{}
// PriorityTagArray specifies the priority tag array for retrieving from high to low.
// If it's given `nil`, it returns map[name]Field, of which the `name` is attribute name.
PriorityTagArray []string
// RecursiveOption specifies the way retrieving the fields recursively if the attribute
// is an embedded struct. It is RecursiveOptionNone in default.
RecursiveOption int
}
// Fields retrieves and returns the fields of `pointer` as slice.
func Fields(in FieldsInput) ([]Field, error) {
var (
@ -169,6 +152,7 @@ func Fields(in FieldsInput) ([]Field, error) {
break
}
fallthrough
case RecursiveOptionEmbedded:
structFields, err := Fields(FieldsInput{
Pointer: field.Value,
@ -249,6 +233,7 @@ func FieldMap(in FieldMapInput) (map[string]Field, error) {
break
}
fallthrough
case RecursiveOptionEmbedded:
m, err := FieldMap(FieldMapInput{
Pointer: field.Value,

View File

@ -4,16 +4,19 @@
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://github.com/gogf/gf.
package structs
package gstructs
import (
"errors"
"reflect"
"strconv"
"github.com/gogf/gf/v2/util/gtag"
)
// ParseTag parses tag string into map.
// For example, tag ParseTag(`v:"required" p:"id" d:"1"`) => map[v:required p:id d:1].
// For example:
// ParseTag(`v:"required" p:"id" d:"1"`) => map[v:required p:id d:1].
func ParseTag(tag string) map[string]string {
var (
key string
@ -60,7 +63,7 @@ func ParseTag(tag string) map[string]string {
if err != nil {
panic(err)
}
data[key] = value
data[key] = gtag.Parse(value)
}
return data
}

View File

@ -4,7 +4,7 @@
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://github.com/gogf/gf.
package structs
package gstructs
import (
"errors"

View File

@ -4,11 +4,12 @@
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://github.com/gogf/gf.
package structs_test
package gstructs_test
import (
"github.com/gogf/gf/v2/internal/structs"
"testing"
"github.com/gogf/gf/v2/os/gstructs"
)
type User struct {
@ -24,12 +25,12 @@ var (
func Benchmark_TagFields(b *testing.B) {
for i := 0; i < b.N; i++ {
structs.TagFields(user, []string{"params", "my-tag1"})
gstructs.TagFields(user, []string{"params", "my-tag1"})
}
}
func Benchmark_TagFields_NilPointer(b *testing.B) {
for i := 0; i < b.N; i++ {
structs.TagFields(&userNilPointer, []string{"params", "my-tag1"})
gstructs.TagFields(&userNilPointer, []string{"params", "my-tag1"})
}
}

View File

@ -4,12 +4,12 @@
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://github.com/gogf/gf.
package structs_test
package gstructs_test
import (
"testing"
"github.com/gogf/gf/v2/internal/structs"
"github.com/gogf/gf/v2/os/gstructs"
"github.com/gogf/gf/v2/frame/g"
@ -24,16 +24,16 @@ func Test_Basic(t *testing.T) {
Pass string `my-tag1:"pass1" my-tag2:"pass2" params:"pass"`
}
var user User
m, _ := structs.TagMapName(user, []string{"params"})
m, _ := gstructs.TagMapName(user, []string{"params"})
t.Assert(m, g.Map{"name": "Name", "pass": "Pass"})
m, _ = structs.TagMapName(&user, []string{"params"})
m, _ = gstructs.TagMapName(&user, []string{"params"})
t.Assert(m, g.Map{"name": "Name", "pass": "Pass"})
m, _ = structs.TagMapName(&user, []string{"params", "my-tag1"})
m, _ = gstructs.TagMapName(&user, []string{"params", "my-tag1"})
t.Assert(m, g.Map{"name": "Name", "pass": "Pass"})
m, _ = structs.TagMapName(&user, []string{"my-tag1", "params"})
m, _ = gstructs.TagMapName(&user, []string{"my-tag1", "params"})
t.Assert(m, g.Map{"name": "Name", "pass1": "Pass"})
m, _ = structs.TagMapName(&user, []string{"my-tag2", "params"})
m, _ = gstructs.TagMapName(&user, []string{"my-tag2", "params"})
t.Assert(m, g.Map{"name": "Name", "pass2": "Pass"})
})
@ -48,7 +48,7 @@ func Test_Basic(t *testing.T) {
Base `params:"base"`
}
user := new(UserWithBase)
m, _ := structs.TagMapName(user, []string{"params"})
m, _ := gstructs.TagMapName(user, []string{"params"})
t.Assert(m, g.Map{
"base": "Base",
"password1": "Pass1",
@ -73,9 +73,9 @@ func Test_Basic(t *testing.T) {
}
user1 := new(UserWithEmbeddedAttribute)
user2 := new(UserWithoutEmbeddedAttribute)
m, _ := structs.TagMapName(user1, []string{"params"})
m, _ := gstructs.TagMapName(user1, []string{"params"})
t.Assert(m, g.Map{"password1": "Pass1", "password2": "Pass2"})
m, _ = structs.TagMapName(user2, []string{"params"})
m, _ = gstructs.TagMapName(user2, []string{"params"})
t.Assert(m, g.Map{})
})
}
@ -88,16 +88,16 @@ func Test_StructOfNilPointer(t *testing.T) {
Pass string `my-tag1:"pass1" my-tag2:"pass2" params:"pass"`
}
var user *User
m, _ := structs.TagMapName(user, []string{"params"})
m, _ := gstructs.TagMapName(user, []string{"params"})
t.Assert(m, g.Map{"name": "Name", "pass": "Pass"})
m, _ = structs.TagMapName(&user, []string{"params"})
m, _ = gstructs.TagMapName(&user, []string{"params"})
t.Assert(m, g.Map{"name": "Name", "pass": "Pass"})
m, _ = structs.TagMapName(&user, []string{"params", "my-tag1"})
m, _ = gstructs.TagMapName(&user, []string{"params", "my-tag1"})
t.Assert(m, g.Map{"name": "Name", "pass": "Pass"})
m, _ = structs.TagMapName(&user, []string{"my-tag1", "params"})
m, _ = gstructs.TagMapName(&user, []string{"my-tag1", "params"})
t.Assert(m, g.Map{"name": "Name", "pass1": "Pass"})
m, _ = structs.TagMapName(&user, []string{"my-tag2", "params"})
m, _ = gstructs.TagMapName(&user, []string{"my-tag2", "params"})
t.Assert(m, g.Map{"name": "Name", "pass2": "Pass"})
})
}
@ -110,7 +110,7 @@ func Test_Fields(t *testing.T) {
Pass string `my-tag1:"pass1" my-tag2:"pass2" params:"pass"`
}
var user *User
fields, _ := structs.Fields(structs.FieldsInput{
fields, _ := gstructs.Fields(gstructs.FieldsInput{
Pointer: user,
RecursiveOption: 0,
})
@ -136,9 +136,9 @@ func Test_Fields_WithEmbedded1(t *testing.T) {
B // Should be put here to validate its index.
Score int64
}
r, err := structs.Fields(structs.FieldsInput{
r, err := gstructs.Fields(gstructs.FieldsInput{
Pointer: new(A),
RecursiveOption: structs.RecursiveOptionEmbeddedNoTag,
RecursiveOption: gstructs.RecursiveOptionEmbeddedNoTag,
})
t.AssertNil(err)
t.Assert(len(r), 4)
@ -172,9 +172,9 @@ func Test_Fields_WithEmbedded2(t *testing.T) {
}
gtest.C(t, func(t *gtest.T) {
r, err := structs.Fields(structs.FieldsInput{
r, err := gstructs.Fields(gstructs.FieldsInput{
Pointer: new(MetaNodeItem),
RecursiveOption: structs.RecursiveOptionEmbeddedNoTag,
RecursiveOption: gstructs.RecursiveOptionEmbeddedNoTag,
})
t.AssertNil(err)
t.Assert(len(r), 4)
@ -201,9 +201,9 @@ func Test_Fields_WithEmbedded_Filter(t *testing.T) {
B // Should be put here to validate its index.
Score int64
}
r, err := structs.Fields(structs.FieldsInput{
r, err := gstructs.Fields(gstructs.FieldsInput{
Pointer: new(A),
RecursiveOption: structs.RecursiveOptionEmbeddedNoTag,
RecursiveOption: gstructs.RecursiveOptionEmbeddedNoTag,
})
t.AssertNil(err)
t.Assert(len(r), 4)
@ -222,10 +222,10 @@ func Test_FieldMap(t *testing.T) {
Pass string `my-tag1:"pass1" my-tag2:"pass2" params:"pass"`
}
var user *User
m, _ := structs.FieldMap(structs.FieldMapInput{
m, _ := gstructs.FieldMap(gstructs.FieldMapInput{
Pointer: user,
PriorityTagArray: []string{"params"},
RecursiveOption: structs.RecursiveOptionEmbedded,
RecursiveOption: gstructs.RecursiveOptionEmbedded,
})
t.Assert(len(m), 3)
_, ok := m["Id"]
@ -246,10 +246,10 @@ func Test_FieldMap(t *testing.T) {
Pass string `my-tag1:"pass1" my-tag2:"pass2" params:"pass"`
}
var user *User
m, _ := structs.FieldMap(structs.FieldMapInput{
m, _ := gstructs.FieldMap(gstructs.FieldMapInput{
Pointer: user,
PriorityTagArray: nil,
RecursiveOption: structs.RecursiveOptionEmbedded,
RecursiveOption: gstructs.RecursiveOptionEmbedded,
})
t.Assert(len(m), 3)
_, ok := m["Id"]
@ -273,9 +273,9 @@ func Test_StructType(t *testing.T) {
type A struct {
B
}
r, err := structs.StructType(new(A))
r, err := gstructs.StructType(new(A))
t.AssertNil(err)
t.Assert(r.Signature(), `github.com/gogf/gf/v2/internal/structs_test/structs_test.A`)
t.Assert(r.Signature(), `github.com/gogf/gf/v2/os/gstructs_test/gstructs_test.A`)
})
gtest.C(t, func(t *gtest.T) {
type B struct {
@ -284,9 +284,9 @@ func Test_StructType(t *testing.T) {
type A struct {
B
}
r, err := structs.StructType(new(A).B)
r, err := gstructs.StructType(new(A).B)
t.AssertNil(err)
t.Assert(r.Signature(), `github.com/gogf/gf/v2/internal/structs_test/structs_test.B`)
t.Assert(r.Signature(), `github.com/gogf/gf/v2/os/gstructs_test/gstructs_test.B`)
})
gtest.C(t, func(t *gtest.T) {
type B struct {
@ -295,9 +295,9 @@ func Test_StructType(t *testing.T) {
type A struct {
*B
}
r, err := structs.StructType(new(A).B)
r, err := gstructs.StructType(new(A).B)
t.AssertNil(err)
t.Assert(r.String(), `structs_test.B`)
t.Assert(r.String(), `gstructs_test.B`)
})
// Error.
gtest.C(t, func(t *gtest.T) {
@ -308,7 +308,7 @@ func Test_StructType(t *testing.T) {
*B
Id int
}
_, err := structs.StructType(new(A).Id)
_, err := gstructs.StructType(new(A).Id)
t.AssertNE(err, nil)
})
}
@ -321,9 +321,9 @@ func Test_StructTypeBySlice(t *testing.T) {
type A struct {
Array []*B
}
r, err := structs.StructType(new(A).Array)
r, err := gstructs.StructType(new(A).Array)
t.AssertNil(err)
t.Assert(r.Signature(), `github.com/gogf/gf/v2/internal/structs_test/structs_test.B`)
t.Assert(r.Signature(), `github.com/gogf/gf/v2/os/gstructs_test/gstructs_test.B`)
})
gtest.C(t, func(t *gtest.T) {
type B struct {
@ -332,9 +332,9 @@ func Test_StructTypeBySlice(t *testing.T) {
type A struct {
Array []B
}
r, err := structs.StructType(new(A).Array)
r, err := gstructs.StructType(new(A).Array)
t.AssertNil(err)
t.Assert(r.Signature(), `github.com/gogf/gf/v2/internal/structs_test/structs_test.B`)
t.Assert(r.Signature(), `github.com/gogf/gf/v2/os/gstructs_test/gstructs_test.B`)
})
gtest.C(t, func(t *gtest.T) {
type B struct {
@ -343,9 +343,9 @@ func Test_StructTypeBySlice(t *testing.T) {
type A struct {
Array *[]B
}
r, err := structs.StructType(new(A).Array)
r, err := gstructs.StructType(new(A).Array)
t.AssertNil(err)
t.Assert(r.Signature(), `github.com/gogf/gf/v2/internal/structs_test/structs_test.B`)
t.Assert(r.Signature(), `github.com/gogf/gf/v2/os/gstructs_test/gstructs_test.B`)
})
}
@ -358,7 +358,7 @@ func TestType_FieldKeys(t *testing.T) {
type A struct {
Array []*B
}
r, err := structs.StructType(new(A).Array)
r, err := gstructs.StructType(new(A).Array)
t.AssertNil(err)
t.Assert(r.FieldKeys(), g.Slice{"Id", "Name"})
})
@ -370,7 +370,7 @@ func TestType_TagMap(t *testing.T) {
Id int `d:"123" description:"I love gf"`
Name string `v:"required" description:"应用Id"`
}
r, err := structs.Fields(structs.FieldsInput{
r, err := gstructs.Fields(gstructs.FieldsInput{
Pointer: new(A),
RecursiveOption: 0,
})

View File

@ -12,7 +12,7 @@ import (
"github.com/gogf/gf/v2/errors/gcode"
"github.com/gogf/gf/v2/errors/gerror"
"github.com/gogf/gf/v2/internal/json"
"github.com/gogf/gf/v2/internal/structs"
"github.com/gogf/gf/v2/os/gstructs"
"github.com/gogf/gf/v2/text/gstr"
"github.com/gogf/gf/v2/util/gconv"
)
@ -43,7 +43,7 @@ type ParameterRef struct {
Value *Parameter
}
func (oai *OpenApiV3) newParameterRefWithStructMethod(field structs.Field, path, method string) (*ParameterRef, error) {
func (oai *OpenApiV3) newParameterRefWithStructMethod(field gstructs.Field, path, method string) (*ParameterRef, error) {
var (
tagMap = field.TagMap()
parameter = &Parameter{

View File

@ -11,7 +11,7 @@ import (
"github.com/gogf/gf/v2/errors/gcode"
"github.com/gogf/gf/v2/errors/gerror"
"github.com/gogf/gf/v2/internal/structs"
"github.com/gogf/gf/v2/os/gstructs"
"github.com/gogf/gf/v2/text/gstr"
"github.com/gogf/gf/v2/util/gconv"
"github.com/gogf/gf/v2/util/gmeta"
@ -182,9 +182,9 @@ func (oai *OpenApiV3) addPath(in addPathInput) error {
}
}
// It also sets request parameters.
structFields, _ := structs.Fields(structs.FieldsInput{
structFields, _ := gstructs.Fields(gstructs.FieldsInput{
Pointer: inputObject.Interface(),
RecursiveOption: structs.RecursiveOptionEmbeddedNoTag,
RecursiveOption: gstructs.RecursiveOptionEmbeddedNoTag,
})
for _, structField := range structFields {
if operation.Parameters == nil {

View File

@ -10,7 +10,7 @@ import (
"reflect"
"github.com/gogf/gf/v2/internal/json"
"github.com/gogf/gf/v2/internal/structs"
"github.com/gogf/gf/v2/os/gstructs"
"github.com/gogf/gf/v2/text/gstr"
)
@ -59,9 +59,9 @@ func (oai *OpenApiV3) getRequestSchemaRef(in getRequestSchemaRefInput) (*SchemaR
schema.Properties[k] = v
}
} else {
structFields, _ := structs.Fields(structs.FieldsInput{
structFields, _ := gstructs.Fields(gstructs.FieldsInput{
Pointer: in.RequestObject,
RecursiveOption: structs.RecursiveOptionEmbeddedNoTag,
RecursiveOption: gstructs.RecursiveOptionEmbeddedNoTag,
})
for _, structField := range structFields {
var (

View File

@ -10,7 +10,7 @@ import (
"reflect"
"github.com/gogf/gf/v2/internal/json"
"github.com/gogf/gf/v2/internal/structs"
"github.com/gogf/gf/v2/os/gstructs"
"github.com/gogf/gf/v2/text/gstr"
)
@ -63,9 +63,9 @@ func (oai *OpenApiV3) getResponseSchemaRef(in getResponseSchemaRefInput) (*Schem
schema.Properties[k] = v
}
} else {
structFields, _ := structs.Fields(structs.FieldsInput{
structFields, _ := gstructs.Fields(gstructs.FieldsInput{
Pointer: in.ResponseObject,
RecursiveOption: structs.RecursiveOptionEmbeddedNoTag,
RecursiveOption: gstructs.RecursiveOptionEmbeddedNoTag,
})
for _, structField := range structFields {
var (

View File

@ -11,7 +11,7 @@ import (
"github.com/gogf/gf/v2/errors/gcode"
"github.com/gogf/gf/v2/errors/gerror"
"github.com/gogf/gf/v2/internal/structs"
"github.com/gogf/gf/v2/os/gstructs"
"github.com/gogf/gf/v2/text/gstr"
"github.com/gogf/gf/v2/util/gconv"
"github.com/gogf/gf/v2/util/gmeta"
@ -105,9 +105,9 @@ func (oai *OpenApiV3) doAddSchemaSingle(object interface{}) error {
// structToSchema converts and returns given struct object as Schema.
func (oai *OpenApiV3) structToSchema(object interface{}) (*Schema, error) {
structFields, _ := structs.Fields(structs.FieldsInput{
structFields, _ := gstructs.Fields(gstructs.FieldsInput{
Pointer: object,
RecursiveOption: structs.RecursiveOptionEmbeddedNoTag,
RecursiveOption: gstructs.RecursiveOptionEmbeddedNoTag,
})
var (
tagMap = gmeta.Data(object)

View File

@ -12,8 +12,8 @@ import (
"github.com/gogf/gf/v2/errors/gcode"
"github.com/gogf/gf/v2/errors/gerror"
"github.com/gogf/gf/v2/internal/structs"
"github.com/gogf/gf/v2/internal/utils"
"github.com/gogf/gf/v2/os/gstructs"
)
// Scan automatically checks the type of `pointer` and converts `params` to `pointer`. It supports `pointer`
@ -384,9 +384,9 @@ func doScanList(structSlice interface{}, structSlicePointer interface{}, bindToA
relationFromAttrField = relationFromAttrValue.FieldByName(relationBindToFieldName)
if !relationFromAttrField.IsValid() {
var (
filedMap, _ = structs.FieldMap(structs.FieldMapInput{
filedMap, _ = gstructs.FieldMap(gstructs.FieldMapInput{
Pointer: relationFromAttrValue,
RecursiveOption: structs.RecursiveOptionEmbeddedNoTag,
RecursiveOption: gstructs.RecursiveOptionEmbeddedNoTag,
})
)
if key, _ := utils.MapPossibleItemByKey(Map(filedMap), relationBindToFieldName); key == "" {

View File

@ -14,8 +14,8 @@ import (
"github.com/gogf/gf/v2/errors/gerror"
"github.com/gogf/gf/v2/internal/empty"
"github.com/gogf/gf/v2/internal/json"
"github.com/gogf/gf/v2/internal/structs"
"github.com/gogf/gf/v2/internal/utils"
"github.com/gogf/gf/v2/os/gstructs"
)
// Struct maps the params key-value pairs to the corresponding struct object's attributes.
@ -237,7 +237,7 @@ func doStruct(params interface{}, pointer interface{}, mapping map[string]string
} else {
priorityTagArray = StructTagPriority
}
tagToNameMap, err := structs.TagMapName(pointerElemReflectValue, priorityTagArray)
tagToNameMap, err := gstructs.TagMapName(pointerElemReflectValue, priorityTagArray)
if err != nil {
return err
}

View File

@ -9,7 +9,7 @@ package gmeta
import (
"github.com/gogf/gf/v2/container/gvar"
"github.com/gogf/gf/v2/internal/structs"
"github.com/gogf/gf/v2/os/gstructs"
)
// Meta is used as an embedded attribute for struct to enabled metadata feature.
@ -22,19 +22,12 @@ const (
// Data retrieves and returns all metadata from `object`.
func Data(object interface{}) map[string]string {
reflectType, err := structs.StructType(object)
reflectType, err := gstructs.StructType(object)
if err != nil {
return nil
}
if field, ok := reflectType.FieldByName(metaAttributeName); ok {
var (
tags = structs.ParseTag(string(field.Tag))
data = make(map[string]string, len(tags))
)
for k, v := range tags {
data[k] = v
}
return data
return gstructs.ParseTag(string(field.Tag))
}
return map[string]string{}
}

60
util/gtag/gtag.go Normal file
View File

@ -0,0 +1,60 @@
// 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 gtag providing tag content storing for struct.
//
// Note that calling functions of this package is concurrently safe.
package gtag
import (
"regexp"
"sync"
)
var (
mu sync.RWMutex
data = make(map[string]string)
regex = regexp.MustCompile(`\{(.+?)\}`)
)
// Set sets tag content for specified name.
func Set(name, value string) {
mu.Lock()
defer mu.Unlock()
data[name] = value
}
// Sets sets multiple tag content by map.
func Sets(m map[string]string) {
mu.Lock()
defer mu.Unlock()
for k, v := range m {
data[k] = v
}
}
// Get retrieves and returns the stored tag content for specified name.
func Get(name string) string {
mu.RLock()
defer mu.RUnlock()
return data[name]
}
// Parse parses and returns the content by replacing all tag name variable to
// its content for given `content`.
// Eg:
// If "Demo:content" in tag mapping,
// Parse(`This is {Demo}`) -> `This is content`.
func Parse(content string) string {
mu.RLock()
defer mu.RUnlock()
return regex.ReplaceAllStringFunc(content, func(s string) string {
if v, ok := data[s[1:len(s)-1]]; ok {
return v
}
return s
})
}

View File

@ -0,0 +1,59 @@
// 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 gtag_test
import (
"fmt"
"testing"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/os/gtime"
"github.com/gogf/gf/v2/test/gtest"
"github.com/gogf/gf/v2/util/gtag"
)
func Test_Set_Get(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
k := gtime.TimestampNanoStr()
v := gtime.TimestampNanoStr()
gtag.Set(k, v)
t.Assert(gtag.Get(k), v)
})
}
func Test_Sets_Get(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
k1 := gtime.TimestampNanoStr()
k2 := gtime.TimestampNanoStr()
v1 := gtime.TimestampNanoStr()
v2 := gtime.TimestampNanoStr()
gtag.Sets(g.MapStrStr{
k1: v1,
k2: v2,
})
t.Assert(gtag.Get(k1), v1)
t.Assert(gtag.Get(k2), v2)
})
}
func Test_Parse(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
var (
k1 = gtime.TimestampNanoStr()
k2 = gtime.TimestampNanoStr()
v1 = gtime.TimestampNanoStr()
v2 = gtime.TimestampNanoStr()
content = fmt.Sprintf(`this is {%s} and {%s}`, k1, k2)
expect = fmt.Sprintf(`this is %s and %s`, v1, v2)
)
gtag.Sets(g.MapStrStr{
k1: v1,
k2: v2,
})
t.Assert(gtag.Parse(content), expect)
})
}

View File

@ -13,7 +13,7 @@ import (
"reflect"
"strings"
"github.com/gogf/gf/v2/internal/structs"
"github.com/gogf/gf/v2/os/gstructs"
"github.com/gogf/gf/v2/text/gstr"
)
@ -261,9 +261,9 @@ func doDumpMap(in doDumpInternalInput) {
}
func doDumpStruct(in doDumpInternalInput) {
structFields, _ := structs.Fields(structs.FieldsInput{
structFields, _ := gstructs.Fields(gstructs.FieldsInput{
Pointer: in.Value,
RecursiveOption: structs.RecursiveOptionEmbedded,
RecursiveOption: gstructs.RecursiveOptionEmbedded,
})
if len(structFields) == 0 {
var (

View File

@ -12,7 +12,7 @@ import (
"strings"
"github.com/gogf/gf/v2/errors/gcode"
"github.com/gogf/gf/v2/internal/structs"
"github.com/gogf/gf/v2/os/gstructs"
"github.com/gogf/gf/v2/util/gconv"
"github.com/gogf/gf/v2/util/gutil"
)
@ -23,17 +23,17 @@ func (v *Validator) doCheckStruct(ctx context.Context, object interface{}) Error
fieldToAliasNameMap = make(map[string]string) // Field names to alias name map.
resultSequenceRules = make([]fieldRule, 0)
)
fieldMap, err := structs.FieldMap(structs.FieldMapInput{
fieldMap, err := gstructs.FieldMap(gstructs.FieldMapInput{
Pointer: object,
PriorityTagArray: aliasNameTagPriority,
RecursiveOption: structs.RecursiveOptionEmbedded,
RecursiveOption: gstructs.RecursiveOptionEmbedded,
})
if err != nil {
return newValidationErrorByStr(internalObjectErrRuleName, err)
}
// It here must use structs.TagFields not structs.FieldMap to ensure error sequence.
tagFields, err := structs.TagFields(object, structTagPriority)
// It here must use gstructs.TagFields not gstructs.FieldMap to ensure error sequence.
tagFields, err := gstructs.TagFields(object, structTagPriority)
if err != nil {
return newValidationErrorByStr(internalObjectErrRuleName, err)
}