From 9e21052a3ce9be589aa125178bde28012e0bd097 Mon Sep 17 00:00:00 2001 From: John Guo Date: Tue, 23 Nov 2021 14:08:37 +0800 Subject: [PATCH] object feature for package gcmd --- internal/structs/structs_field.go | 18 +-- internal/structs/structs_tag.go | 20 ++-- os/gcmd/gcmd_command_object.go | 124 ++++++++++++++------- os/gcmd/gcmd_z_unit_feature_object_test.go | 4 +- protocol/goai/goai_parameter.go | 2 +- 5 files changed, 108 insertions(+), 60 deletions(-) diff --git a/internal/structs/structs_field.go b/internal/structs/structs_field.go index 518258b00..e2317f09c 100644 --- a/internal/structs/structs_field.go +++ b/internal/structs/structs_field.go @@ -123,7 +123,7 @@ type FieldMapInput 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. + // 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 @@ -132,12 +132,12 @@ type FieldMapInput struct { } // Fields retrieves and returns the fields of `pointer` as slice. -func Fields(in FieldsInput) ([]*Field, error) { +func Fields(in FieldsInput) ([]Field, error) { var ( ok bool fieldFilterMap = make(map[string]struct{}) - retrievedFields = make([]*Field, 0) - currentLevelFieldMap = make(map[string]*Field) + retrievedFields = make([]Field, 0) + currentLevelFieldMap = make(map[string]Field) ) rangeFields, err := getFieldValues(in.Pointer) if err != nil { @@ -187,7 +187,7 @@ func Fields(in FieldsInput) ([]*Field, error) { continue } fieldFilterMap[fieldName] = struct{}{} - if v := currentLevelFieldMap[fieldName]; v == nil { + if v, ok := currentLevelFieldMap[fieldName]; !ok { retrievedFields = append(retrievedFields, structField) } else { retrievedFields = append(retrievedFields, v) @@ -204,25 +204,25 @@ func Fields(in FieldsInput) ([]*Field, error) { return retrievedFields, nil } -// FieldMap retrieves and returns struct field as map[name/tag]*Field from `pointer`. +// FieldMap retrieves and returns struct field as map[name/tag]Field from `pointer`. // // The parameter `pointer` should be type of struct/*struct. // // The parameter `priority` 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. +// If it's given `nil`, it returns map[name]Field, of which the `name` is attribute name. // // The parameter `recursive` specifies the whether retrieving the fields recursively if the attribute // is an embedded struct. // // Note that it only retrieves the exported attributes with first letter up-case from struct. -func FieldMap(in FieldMapInput) (map[string]*Field, error) { +func FieldMap(in FieldMapInput) (map[string]Field, error) { fields, err := getFieldValues(in.Pointer) if err != nil { return nil, err } var ( tagValue = "" - mapField = make(map[string]*Field) + mapField = make(map[string]Field) ) for _, field := range fields { // Only retrieve exported attributes. diff --git a/internal/structs/structs_tag.go b/internal/structs/structs_tag.go index da56e4a47..299c3ba2c 100644 --- a/internal/structs/structs_tag.go +++ b/internal/structs/structs_tag.go @@ -65,14 +65,14 @@ func ParseTag(tag string) map[string]string { return data } -// TagFields retrieves and returns struct tags as []*Field from `pointer`. +// TagFields retrieves and returns struct tags as []Field from `pointer`. // // The parameter `pointer` should be type of struct/*struct. // // Note that, // 1. It only retrieves the exported attributes with first letter up-case from struct. // 2. The parameter `priority` should be given, it only retrieves fields that has given tag. -func TagFields(pointer interface{}, priority []string) ([]*Field, error) { +func TagFields(pointer interface{}, priority []string) ([]Field, error) { return getFieldValuesByTagPriority(pointer, priority, map[string]struct{}{}) } @@ -95,18 +95,18 @@ func TagMapName(pointer interface{}, priority []string) (map[string]string, erro return tagMap, nil } -// TagMapField retrieves struct tags as map[tag]*Field from `pointer`, and returns it. +// TagMapField retrieves struct tags as map[tag]Field from `pointer`, and returns it. // The parameter `object` should be either type of struct/*struct/[]struct/[]*struct. // // Note that, // 1. It only retrieves the exported attributes with first letter up-case from struct. // 2. The parameter `priority` should be given, it only retrieves fields that has given tag. -func TagMapField(object interface{}, priority []string) (map[string]*Field, error) { +func TagMapField(object interface{}, priority []string) (map[string]Field, error) { fields, err := TagFields(object, priority) if err != nil { return nil, err } - tagMap := make(map[string]*Field, len(fields)) + tagMap := make(map[string]Field, len(fields)) for _, field := range fields { tagField := field tagMap[field.TagValue] = tagField @@ -114,7 +114,7 @@ func TagMapField(object interface{}, priority []string) (map[string]*Field, erro return tagMap, nil } -func getFieldValues(value interface{}) ([]*Field, error) { +func getFieldValues(value interface{}) ([]Field, error) { var ( reflectValue reflect.Value reflectKind reflect.Kind @@ -156,10 +156,10 @@ exitLoop: var ( structType = reflectValue.Type() length = reflectValue.NumField() - fields = make([]*Field, length) + fields = make([]Field, length) ) for i := 0; i < length; i++ { - fields[i] = &Field{ + fields[i] = Field{ Value: reflectValue.Field(i), Field: structType.Field(i), } @@ -167,14 +167,14 @@ exitLoop: return fields, nil } -func getFieldValuesByTagPriority(pointer interface{}, priority []string, tagMap map[string]struct{}) ([]*Field, error) { +func getFieldValuesByTagPriority(pointer interface{}, priority []string, tagMap map[string]struct{}) ([]Field, error) { fields, err := getFieldValues(pointer) if err != nil { return nil, err } var ( tagValue = "" - tagFields = make([]*Field, 0) + tagFields = make([]Field, 0) ) for _, field := range fields { // Only retrieve exported attributes. diff --git a/os/gcmd/gcmd_command_object.go b/os/gcmd/gcmd_command_object.go index 9b569f474..1c80e3f87 100644 --- a/os/gcmd/gcmd_command_object.go +++ b/os/gcmd/gcmd_command_object.go @@ -12,53 +12,42 @@ 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/text/gstr" + "github.com/gogf/gf/v2/util/gconv" "github.com/gogf/gf/v2/util/gmeta" + "github.com/gogf/gf/v2/util/gutil" ) const ( - tagNameName = `name` - tagNameUsage = `usage` - tagNameBrief = `brief` - tagNameShort = `short` - tagNameOrphan = `orphan` - tagNameDescription = `description` - tagNameDc = `dc` - tagNameAddition = `additional` - tagNameAd = `ad` + tagNameDc = `dc` + tagNameAd = `ad` ) -func (c *Command) AddObject(objects ...interface{}) (err error) { - for _, object := range objects { - if err = c.doAddObject(object); err != nil { - return err - } - } - return nil -} - -func (c *Command) doAddObject(object interface{}) error { +func CommandsFromObject(object interface{}) (commands []Command, err error) { originValueAndKind := utils.OriginValueAndKind(object) if originValueAndKind.OriginKind != reflect.Struct { - return gerror.Newf( + return nil, gerror.Newf( `input object should be type of struct, but got "%s"`, originValueAndKind.InputValue.Type().String(), ) } - for i := 0; i < originValueAndKind.InputValue.NumMethod(); i++ { - method := originValueAndKind.InputValue.Method(i) - } - for _, field := range fields { - - } - return nil + //for i := 0; i < originValueAndKind.InputValue.NumMethod(); i++ { + // method := originValueAndKind.InputValue.Method(i) + //} + //for _, field := range fields { + // + //} + return } -func newCommandFromMethod(object, method reflect.Value) (cmd *Command, err error) { +func newCommandFromMethod(object, method reflect.Value) (*Command, error) { var ( + err error reflectType = method.Type() ) + // Necessary validation for input/output parameters and naming. if reflectType.NumIn() != 2 || reflectType.NumOut() != 2 { if reflectType.PkgPath() != "" { err = gerror.NewCodef( @@ -73,27 +62,24 @@ func newCommandFromMethod(object, method reflect.Value) (cmd *Command, err error reflectType.String(), ) } - return + return nil, err } - if reflectType.In(0).String() != "context.Context" { err = gerror.NewCodef( gcode.CodeInvalidParameter, `invalid handler: defined as "%s", but the first input parameter should be type of "context.Context"`, reflectType.String(), ) - return + return nil, err } - if reflectType.Out(1).String() != "error" { err = gerror.NewCodef( gcode.CodeInvalidParameter, `invalid handler: defined as "%s", but the last output parameter should be type of "error"`, reflectType.String(), ) - return + return nil, err } - // The input struct should be named as `xxxInput`. if !gstr.HasSuffix(reflectType.In(1).String(), `Input`) { err = gerror.NewCodef( @@ -101,9 +87,8 @@ func newCommandFromMethod(object, method reflect.Value) (cmd *Command, err error `invalid struct naming for input: defined as "%s", but it should be named with "Input" suffix like "xxxInput"`, reflectType.In(1).String(), ) - return + return nil, err } - // The output struct should be named as `xxxOutput`. if !gstr.HasSuffix(reflectType.Out(0).String(), `Output`) { err = gerror.NewCodef( @@ -111,9 +96,72 @@ func newCommandFromMethod(object, method reflect.Value) (cmd *Command, err error `invalid struct naming for output: defined as "%s", but it should be named with "Output" suffix like "xxxOutput"`, reflectType.Out(0).String(), ) - return + return nil, err } + var ( - metaMap = gmeta.Data() + inputObject reflect.Value + outputObject reflect.Value ) + if method.Type().In(1).Kind() == reflect.Ptr { + inputObject = reflect.New(method.Type().In(1).Elem()).Elem() + } else { + inputObject = reflect.New(method.Type().In(1)).Elem() + } + + if method.Type().Out(1).Kind() == reflect.Ptr { + outputObject = reflect.New(method.Type().Out(0).Elem()).Elem() + } else { + outputObject = reflect.New(method.Type().Out(0)).Elem() + } + // Command creating. + var ( + cmd = Command{} + metaData = gmeta.Data(inputObject.Interface()) + ) + if err = gconv.Scan(metaData, &cmd); err != nil { + return nil, err + } + // Name filed is necessary. + if cmd.Name == "" { + return nil, gerror.Newf( + `command name cannot be empty, "name" tag not found in struct "%s"`, + inputObject.Type().String(), + ) + } + if cmd.Description == "" { + cmd.Description = metaData[tagNameDc] + } + if cmd.Additional == "" { + cmd.Additional = metaData[tagNameAd] + } + + if cmd.Options, err = newOptionsFromInput(inputObject.Interface()); err != nil { + return nil, err + } + return &cmd, nil +} + +func newOptionsFromInput(object interface{}) (options []Option, err error) { + var ( + fields []structs.Field + ) + fields, err = structs.Fields(structs.FieldsInput{ + Pointer: object, + RecursiveOption: structs.RecursiveOptionEmbeddedNoTag, + }) + for _, field := range fields { + var ( + option = Option{} + metaData = gmeta.Data(field.Value.Interface()) + ) + if err = gconv.Scan(metaData, &option); err != nil { + return nil, err + } + if option.Name == "" { + option.Name = field.Name() + } + options = append(options, option) + } + return } diff --git a/os/gcmd/gcmd_z_unit_feature_object_test.go b/os/gcmd/gcmd_z_unit_feature_object_test.go index 1af5dba9e..986f361a1 100644 --- a/os/gcmd/gcmd_z_unit_feature_object_test.go +++ b/os/gcmd/gcmd_z_unit_feature_object_test.go @@ -28,13 +28,13 @@ type TestCmdObjectOutput struct{} type TestCmdObjectEnvInput struct { g.Meta `name:"env" usage:"gf env/test" brief:"gf env command" dc:"description" ad:"ad"` - Name string `v:"required" name:"name" short:"n" orphan:"false" brief:"name for command"` + Name string `v:"required" short:"n" orphan:"false" brief:"name for command"` } type TestCmdObjectEnvOutput struct{} type TestCmdObjectTestInput struct { g.Meta `name:"test" usage:"gf env/test" brief:"gf test command" dc:"description" ad:"ad"` - Name string `v:"required" name:"name" short:"n" orphan:"false" brief:"name for command"` + Name string `v:"required" short:"n" orphan:"false" brief:"name for command"` } type TestCmdObjectTestOutput struct{} diff --git a/protocol/goai/goai_parameter.go b/protocol/goai/goai_parameter.go index 234b9a00a..0cd8e1a0b 100644 --- a/protocol/goai/goai_parameter.go +++ b/protocol/goai/goai_parameter.go @@ -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 structs.Field, path, method string) (*ParameterRef, error) { var ( tagMap = field.TagMap() parameter = &Parameter{