Files
gf/net/goai/goai_shema.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

310 lines
9.7 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 goai
import (
"reflect"
"strings"
"github.com/gogf/gf/v2/container/gmap"
"github.com/gogf/gf/v2/container/gset"
"github.com/gogf/gf/v2/errors/gerror"
"github.com/gogf/gf/v2/internal/json"
"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"
"github.com/gogf/gf/v2/util/gvalid"
)
// Schema is specified by OpenAPI/Swagger 3.0 standard.
type Schema struct {
OneOf SchemaRefs `json:"oneOf,omitempty"`
AnyOf SchemaRefs `json:"anyOf,omitempty"`
AllOf SchemaRefs `json:"allOf,omitempty"`
Not *SchemaRef `json:"not,omitempty"`
Type string `json:"type,omitempty"`
Title string `json:"title,omitempty"`
Format string `json:"format,omitempty"`
Description string `json:"description,omitempty"`
Enum []any `json:"enum,omitempty"`
Default any `json:"default,omitempty"`
Example any `json:"example,omitempty"`
ExternalDocs *ExternalDocs `json:"externalDocs,omitempty"`
UniqueItems bool `json:"uniqueItems,omitempty"`
ExclusiveMin bool `json:"exclusiveMinimum,omitempty"`
ExclusiveMax bool `json:"exclusiveMaximum,omitempty"`
Nullable bool `json:"nullable,omitempty"`
ReadOnly bool `json:"readOnly,omitempty"`
WriteOnly bool `json:"writeOnly,omitempty"`
AllowEmptyValue bool `json:"allowEmptyValue,omitempty"`
XML any `json:"xml,omitempty"`
Deprecated bool `json:"deprecated,omitempty"`
Min *float64 `json:"minimum,omitempty"`
Max *float64 `json:"maximum,omitempty"`
MultipleOf *float64 `json:"multipleOf,omitempty"`
MinLength uint64 `json:"minLength,omitempty"`
MaxLength *uint64 `json:"maxLength,omitempty"`
Pattern string `json:"pattern,omitempty"`
MinItems uint64 `json:"minItems,omitempty"`
MaxItems *uint64 `json:"maxItems,omitempty"`
Items *SchemaRef `json:"items,omitempty"`
Required []string `json:"required,omitempty"`
Properties *Schemas `json:"properties,omitempty"`
MinProps uint64 `json:"minProperties,omitempty"`
MaxProps *uint64 `json:"maxProperties,omitempty"`
AdditionalProperties *SchemaRef `json:"additionalProperties,omitempty"`
Discriminator *Discriminator `json:"discriminator,omitempty"`
XExtensions XExtensions `json:"-"`
ValidationRules string `json:"-"`
}
// Clone only clones necessary attributes.
// TODO clone all attributes, or improve package deepcopy.
func (s *Schema) Clone() *Schema {
newSchema := *s
newSchema.Required = make([]string, len(s.Required))
copy(newSchema.Required, s.Required)
newSchema.Properties = s.Properties.Clone()
return &newSchema
}
func (s Schema) MarshalJSON() ([]byte, error) {
var (
b []byte
m map[string]json.RawMessage
err error
)
type tempSchema Schema // To prevent JSON marshal recursion error.
if b, err = json.Marshal(tempSchema(s)); err != nil {
return nil, err
}
if err = json.Unmarshal(b, &m); err != nil {
return nil, err
}
for k, v := range s.XExtensions {
if b, err = json.Marshal(v); err != nil {
return nil, err
}
m[k] = b
}
return json.Marshal(m)
}
// Discriminator is specified by OpenAPI/Swagger standard version 3.0.
type Discriminator struct {
PropertyName string `json:"propertyName"`
Mapping map[string]string `json:"mapping,omitempty"`
}
// addSchema creates schemas with objects.
// Note that the `object` can be array alias like: `type Res []Item`.
func (oai *OpenApiV3) addSchema(object ...any) error {
for _, v := range object {
if err := oai.doAddSchemaSingle(v); err != nil {
return err
}
}
return nil
}
func (oai *OpenApiV3) doAddSchemaSingle(object any) error {
if oai.Components.Schemas.refs == nil {
oai.Components.Schemas.refs = gmap.NewListMap()
}
var (
reflectType = reflect.TypeOf(object)
structTypeName = oai.golangTypeToSchemaName(reflectType)
)
// Already added.
if oai.Components.Schemas.Get(structTypeName) != nil {
return nil
}
// Take the holder first.
oai.Components.Schemas.Set(structTypeName, SchemaRef{})
schema, err := oai.structToSchema(object)
if err != nil {
return err
}
oai.Components.Schemas.Set(structTypeName, SchemaRef{
Ref: "",
Value: schema,
})
return nil
}
// structToSchema converts and returns given struct object as Schema.
func (oai *OpenApiV3) structToSchema(object any) (*Schema, error) {
var (
tagMap = gmeta.Data(object)
schema = &Schema{
Properties: createSchemas(),
XExtensions: make(XExtensions),
}
ignoreProperties []any
)
if len(tagMap) > 0 {
if err := oai.tagMapToSchema(tagMap, schema); err != nil {
return nil, err
}
}
if schema.Type != "" && schema.Type != TypeObject {
return schema, nil
}
// []struct.
if utils.IsArray(object) {
schema.Type = TypeArray
subSchemaRef, err := oai.newSchemaRefWithGolangType(reflect.TypeOf(object).Elem(), nil)
if err != nil {
return nil, err
}
schema.Items = subSchemaRef
if len(schema.Enum) > 0 {
schema.Items.Value.Enum = schema.Enum
schema.Enum = nil
}
return schema, nil
}
// struct.
structFields, _ := gstructs.Fields(gstructs.FieldsInput{
Pointer: object,
RecursiveOption: gstructs.RecursiveOptionEmbedded,
})
schema.Type = TypeObject
for _, structField := range structFields {
if !gstr.IsLetterUpper(structField.Name()[0]) {
continue
}
var fieldName = structField.TagPriorityName()
fieldName = gstr.Split(gstr.Trim(fieldName), ",")[0]
if fieldName == "" {
fieldName = structField.Name()
}
schemaRef, err := oai.newSchemaRefWithGolangType(
structField.Type().Type,
structField.TagMap(),
)
if err != nil {
return nil, err
}
schema.Properties.Set(fieldName, *schemaRef)
}
schema.Properties.Iterator(func(key string, ref SchemaRef) bool {
if ref.Value != nil && ref.Value.ValidationRules != "" {
validationRuleSet := gset.NewStrSetFrom(gstr.Split(ref.Value.ValidationRules, "|"))
if validationRuleSet.Contains(validationRuleKeyForRequired) {
schema.Required = append(schema.Required, key)
}
// Extract validation rules to schema. like min, max, length
lstRules := gstr.Split(ref.Value.ValidationRules, "|")
for _, rule := range lstRules {
if strings.HasPrefix(rule, validationRuleKeyForMax) {
if ref.Value.Type == "integer" || ref.Value.Type == "number" {
f := gconv.Float64(rule[4:])
ref.Value.Max = &f
}
}
if strings.HasPrefix(rule, validationRuleKeyForMaxLength) {
maxlength := gconv.Uint64(rule[11:])
ref.Value.MaxLength = &maxlength
}
if strings.HasPrefix(rule, validationRuleKeyForMin) {
if ref.Value.Type == "integer" || ref.Value.Type == "number" {
f := gconv.Float64(rule[4:])
ref.Value.Min = &f
}
}
if strings.HasPrefix(rule, validationRuleKeyForMinLength) {
minlength := gconv.Uint64(rule[11:])
ref.Value.MinLength = minlength
}
if strings.HasPrefix(rule, validationRuleKeyForLength) {
lengthRule := gstr.Split(rule[7:], ",")
if len(lengthRule) == 2 {
minlength := gconv.Uint64(lengthRule[0])
ref.Value.MinLength = minlength
maxlength := gconv.Uint64(lengthRule[1])
ref.Value.MaxLength = &maxlength
}
}
if strings.HasPrefix(rule, validationRuleKeyForBetween) {
if ref.Value.Type == "integer" || ref.Value.Type == "number" {
lengthRule := gstr.Split(rule[8:], ",")
if len(lengthRule) == 2 {
minimum := gconv.Float64(lengthRule[0])
ref.Value.Min = &minimum
maximum := gconv.Float64(lengthRule[1])
ref.Value.Max = &maximum
}
}
}
}
}
if !isValidParameterName(key) {
ignoreProperties = append(ignoreProperties, key)
}
return true
})
if len(ignoreProperties) > 0 {
schema.Properties.Removes(ignoreProperties)
}
return schema, nil
}
func (oai *OpenApiV3) tagMapToSchema(tagMap map[string]string, schema *Schema) error {
var mergedTagMap = oai.fillMapWithShortTags(tagMap)
if err := gconv.Struct(mergedTagMap, schema); err != nil {
return gerror.Wrap(err, `mapping struct tags to Schema failed`)
}
oai.tagMapToXExtensions(mergedTagMap, schema.XExtensions)
// Validation info to OpenAPI schema pattern.
for _, tag := range gvalid.GetTags() {
if validationTagValue, ok := tagMap[tag]; ok {
_, validationRules, _ := gvalid.ParseTagValue(validationTagValue)
schema.ValidationRules = validationRules
// Enum checks.
if len(schema.Enum) == 0 {
for _, rule := range gstr.SplitAndTrim(validationRules, "|") {
if gstr.HasPrefix(rule, validationRuleKeyForIn) {
var (
isAllEnumNumber = true
enumArray = gstr.SplitAndTrim(rule[len(validationRuleKeyForIn):], ",")
)
for _, enum := range enumArray {
if !gstr.IsNumeric(enum) {
isAllEnumNumber = false
break
}
}
if isAllEnumNumber {
schema.Enum = gconv.Interfaces(gconv.Int64s(enumArray))
} else {
schema.Enum = gconv.Interfaces(enumArray)
}
}
}
}
break
}
}
return nil
}