forked from ebhomengo/niki
1
0
Fork 0
niki/vendor/github.com/swaggo/swag/field_parser.go

1081 lines
14 KiB
Go
Raw Normal View History

2024-05-14 13:07:09 +00:00
package swag
import (
"fmt"
"go/ast"
"reflect"
"regexp"
"strconv"
"strings"
"sync"
"unicode"
"github.com/go-openapi/spec"
)
var _ FieldParser = &tagBaseFieldParser{p: nil, field: nil, tag: ""}
const (
requiredLabel = "required"
optionalLabel = "optional"
swaggerTypeTag = "swaggertype"
2024-05-14 13:07:09 +00:00
swaggerIgnoreTag = "swaggerignore"
)
type tagBaseFieldParser struct {
p *Parser
2024-05-14 13:07:09 +00:00
field *ast.Field
tag reflect.StructTag
2024-05-14 13:07:09 +00:00
}
func newTagBaseFieldParser(p *Parser, field *ast.Field) FieldParser {
2024-05-14 13:07:09 +00:00
fieldParser := tagBaseFieldParser{
p: p,
2024-05-14 13:07:09 +00:00
field: field,
tag: "",
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
if fieldParser.field.Tag != nil {
2024-05-14 13:07:09 +00:00
fieldParser.tag = reflect.StructTag(strings.ReplaceAll(field.Tag.Value, "`", ""))
2024-05-14 13:07:09 +00:00
}
return &fieldParser
2024-05-14 13:07:09 +00:00
}
func (ps *tagBaseFieldParser) ShouldSkip() bool {
2024-05-14 13:07:09 +00:00
// Skip non-exported fields.
2024-05-14 13:07:09 +00:00
if ps.field.Names != nil && !ast.IsExported(ps.field.Names[0].Name) {
2024-05-14 13:07:09 +00:00
return true
2024-05-14 13:07:09 +00:00
}
if ps.field.Tag == nil {
2024-05-14 13:07:09 +00:00
return false
2024-05-14 13:07:09 +00:00
}
ignoreTag := ps.tag.Get(swaggerIgnoreTag)
2024-05-14 13:07:09 +00:00
if strings.EqualFold(ignoreTag, "true") {
2024-05-14 13:07:09 +00:00
return true
2024-05-14 13:07:09 +00:00
}
// json:"tag,hoge"
2024-05-14 13:07:09 +00:00
name := strings.TrimSpace(strings.Split(ps.tag.Get(jsonTag), ",")[0])
2024-05-14 13:07:09 +00:00
if name == "-" {
2024-05-14 13:07:09 +00:00
return true
2024-05-14 13:07:09 +00:00
}
return false
2024-05-14 13:07:09 +00:00
}
func (ps *tagBaseFieldParser) FieldName() (string, error) {
2024-05-14 13:07:09 +00:00
var name string
if ps.field.Tag != nil {
2024-05-14 13:07:09 +00:00
// json:"tag,hoge"
2024-05-14 13:07:09 +00:00
name = strings.TrimSpace(strings.Split(ps.tag.Get(jsonTag), ",")[0])
2024-05-14 13:07:09 +00:00
if name != "" {
2024-05-14 13:07:09 +00:00
return name, nil
2024-05-14 13:07:09 +00:00
}
// use "form" tag over json tag
2024-05-14 13:07:09 +00:00
name = ps.FormName()
2024-05-14 13:07:09 +00:00
if name != "" {
2024-05-14 13:07:09 +00:00
return name, nil
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
}
if ps.field.Names == nil {
2024-05-14 13:07:09 +00:00
return "", nil
2024-05-14 13:07:09 +00:00
}
switch ps.p.PropNamingStrategy {
2024-05-14 13:07:09 +00:00
case SnakeCase:
2024-05-14 13:07:09 +00:00
return toSnakeCase(ps.field.Names[0].Name), nil
2024-05-14 13:07:09 +00:00
case PascalCase:
2024-05-14 13:07:09 +00:00
return ps.field.Names[0].Name, nil
2024-05-14 13:07:09 +00:00
default:
2024-05-14 13:07:09 +00:00
return toLowerCamelCase(ps.field.Names[0].Name), nil
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
}
func (ps *tagBaseFieldParser) firstTagValue(tag string) string {
2024-05-14 13:07:09 +00:00
if ps.field.Tag != nil {
2024-05-14 13:07:09 +00:00
return strings.TrimRight(strings.TrimSpace(strings.Split(ps.tag.Get(tag), ",")[0]), "[]")
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
return ""
2024-05-14 13:07:09 +00:00
}
func (ps *tagBaseFieldParser) FormName() string {
2024-05-14 13:07:09 +00:00
return ps.firstTagValue(formTag)
2024-05-14 13:07:09 +00:00
}
func (ps *tagBaseFieldParser) HeaderName() string {
2024-05-14 13:07:09 +00:00
return ps.firstTagValue(headerTag)
2024-05-14 13:07:09 +00:00
}
func (ps *tagBaseFieldParser) PathName() string {
2024-05-14 13:07:09 +00:00
return ps.firstTagValue(uriTag)
2024-05-14 13:07:09 +00:00
}
func toSnakeCase(in string) string {
2024-05-14 13:07:09 +00:00
var (
runes = []rune(in)
2024-05-14 13:07:09 +00:00
length = len(runes)
out []rune
2024-05-14 13:07:09 +00:00
)
for idx := 0; idx < length; idx++ {
2024-05-14 13:07:09 +00:00
if idx > 0 && unicode.IsUpper(runes[idx]) &&
2024-05-14 13:07:09 +00:00
((idx+1 < length && unicode.IsLower(runes[idx+1])) || unicode.IsLower(runes[idx-1])) {
2024-05-14 13:07:09 +00:00
out = append(out, '_')
2024-05-14 13:07:09 +00:00
}
out = append(out, unicode.ToLower(runes[idx]))
2024-05-14 13:07:09 +00:00
}
return string(out)
2024-05-14 13:07:09 +00:00
}
func toLowerCamelCase(in string) string {
2024-05-14 13:07:09 +00:00
var flag bool
out := make([]rune, len(in))
runes := []rune(in)
2024-05-14 13:07:09 +00:00
for i, curr := range runes {
2024-05-14 13:07:09 +00:00
if (i == 0 && unicode.IsUpper(curr)) || (flag && unicode.IsUpper(curr)) {
2024-05-14 13:07:09 +00:00
out[i] = unicode.ToLower(curr)
2024-05-14 13:07:09 +00:00
flag = true
continue
2024-05-14 13:07:09 +00:00
}
out[i] = curr
2024-05-14 13:07:09 +00:00
flag = false
2024-05-14 13:07:09 +00:00
}
return string(out)
2024-05-14 13:07:09 +00:00
}
func (ps *tagBaseFieldParser) CustomSchema() (*spec.Schema, error) {
2024-05-14 13:07:09 +00:00
if ps.field.Tag == nil {
2024-05-14 13:07:09 +00:00
return nil, nil
2024-05-14 13:07:09 +00:00
}
typeTag := ps.tag.Get(swaggerTypeTag)
2024-05-14 13:07:09 +00:00
if typeTag != "" {
2024-05-14 13:07:09 +00:00
return BuildCustomSchema(strings.Split(typeTag, ","))
2024-05-14 13:07:09 +00:00
}
return nil, nil
2024-05-14 13:07:09 +00:00
}
type structField struct {
schemaType string
arrayType string
formatType string
maximum *float64
minimum *float64
multipleOf *float64
maxLength *int64
minLength *int64
maxItems *int64
minItems *int64
2024-05-14 13:07:09 +00:00
exampleValue interface{}
enums []interface{}
2024-05-14 13:07:09 +00:00
enumVarNames []interface{}
unique bool
2024-05-14 13:07:09 +00:00
}
// splitNotWrapped slices s into all substrings separated by sep if sep is not
2024-05-14 13:07:09 +00:00
// wrapped by brackets and returns a slice of the substrings between those separators.
2024-05-14 13:07:09 +00:00
func splitNotWrapped(s string, sep rune) []string {
2024-05-14 13:07:09 +00:00
openCloseMap := map[rune]rune{
2024-05-14 13:07:09 +00:00
'(': ')',
2024-05-14 13:07:09 +00:00
'[': ']',
2024-05-14 13:07:09 +00:00
'{': '}',
}
var (
result = make([]string, 0)
current = strings.Builder{}
2024-05-14 13:07:09 +00:00
openCount = 0
openChar rune
2024-05-14 13:07:09 +00:00
)
for _, char := range s {
2024-05-14 13:07:09 +00:00
switch {
2024-05-14 13:07:09 +00:00
case openChar == 0 && openCloseMap[char] != 0:
2024-05-14 13:07:09 +00:00
openChar = char
openCount++
current.WriteRune(char)
2024-05-14 13:07:09 +00:00
case char == openChar:
2024-05-14 13:07:09 +00:00
openCount++
current.WriteRune(char)
2024-05-14 13:07:09 +00:00
case openCount > 0 && char == openCloseMap[openChar]:
2024-05-14 13:07:09 +00:00
openCount--
current.WriteRune(char)
2024-05-14 13:07:09 +00:00
case openCount == 0 && char == sep:
2024-05-14 13:07:09 +00:00
result = append(result, current.String())
openChar = 0
current = strings.Builder{}
2024-05-14 13:07:09 +00:00
default:
2024-05-14 13:07:09 +00:00
current.WriteRune(char)
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
}
if current.String() != "" {
2024-05-14 13:07:09 +00:00
result = append(result, current.String())
2024-05-14 13:07:09 +00:00
}
return result
2024-05-14 13:07:09 +00:00
}
// ComplementSchema complement schema with field properties
2024-05-14 13:07:09 +00:00
func (ps *tagBaseFieldParser) ComplementSchema(schema *spec.Schema) error {
2024-05-14 13:07:09 +00:00
types := ps.p.GetSchemaTypePath(schema, 2)
2024-05-14 13:07:09 +00:00
if len(types) == 0 {
2024-05-14 13:07:09 +00:00
return fmt.Errorf("invalid type for field: %s", ps.field.Names[0])
2024-05-14 13:07:09 +00:00
}
if IsRefSchema(schema) {
2024-05-14 13:07:09 +00:00
var newSchema = spec.Schema{}
2024-05-14 13:07:09 +00:00
err := ps.complementSchema(&newSchema, types)
2024-05-14 13:07:09 +00:00
if err != nil {
2024-05-14 13:07:09 +00:00
return err
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
if !reflect.ValueOf(newSchema).IsZero() {
2024-05-14 13:07:09 +00:00
*schema = *(newSchema.WithAllOf(*schema))
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
return nil
2024-05-14 13:07:09 +00:00
}
return ps.complementSchema(schema, types)
2024-05-14 13:07:09 +00:00
}
// complementSchema complement schema with field properties
2024-05-14 13:07:09 +00:00
func (ps *tagBaseFieldParser) complementSchema(schema *spec.Schema, types []string) error {
2024-05-14 13:07:09 +00:00
if ps.field.Tag == nil {
2024-05-14 13:07:09 +00:00
if ps.field.Doc != nil {
2024-05-14 13:07:09 +00:00
schema.Description = strings.TrimSpace(ps.field.Doc.Text())
2024-05-14 13:07:09 +00:00
}
if schema.Description == "" && ps.field.Comment != nil {
2024-05-14 13:07:09 +00:00
schema.Description = strings.TrimSpace(ps.field.Comment.Text())
2024-05-14 13:07:09 +00:00
}
return nil
2024-05-14 13:07:09 +00:00
}
field := &structField{
2024-05-14 13:07:09 +00:00
schemaType: types[0],
2024-05-14 13:07:09 +00:00
formatType: ps.tag.Get(formatTag),
}
if len(types) > 1 && (types[0] == ARRAY || types[0] == OBJECT) {
2024-05-14 13:07:09 +00:00
field.arrayType = types[1]
2024-05-14 13:07:09 +00:00
}
jsonTagValue := ps.tag.Get(jsonTag)
bindingTagValue := ps.tag.Get(bindingTag)
2024-05-14 13:07:09 +00:00
if bindingTagValue != "" {
2024-05-14 13:07:09 +00:00
parseValidTags(bindingTagValue, field)
2024-05-14 13:07:09 +00:00
}
validateTagValue := ps.tag.Get(validateTag)
2024-05-14 13:07:09 +00:00
if validateTagValue != "" {
2024-05-14 13:07:09 +00:00
parseValidTags(validateTagValue, field)
2024-05-14 13:07:09 +00:00
}
enumsTagValue := ps.tag.Get(enumsTag)
2024-05-14 13:07:09 +00:00
if enumsTagValue != "" {
2024-05-14 13:07:09 +00:00
err := parseEnumTags(enumsTagValue, field)
2024-05-14 13:07:09 +00:00
if err != nil {
2024-05-14 13:07:09 +00:00
return err
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
}
if IsNumericType(field.schemaType) || IsNumericType(field.arrayType) {
2024-05-14 13:07:09 +00:00
maximum, err := getFloatTag(ps.tag, maximumTag)
2024-05-14 13:07:09 +00:00
if err != nil {
2024-05-14 13:07:09 +00:00
return err
2024-05-14 13:07:09 +00:00
}
if maximum != nil {
2024-05-14 13:07:09 +00:00
field.maximum = maximum
2024-05-14 13:07:09 +00:00
}
minimum, err := getFloatTag(ps.tag, minimumTag)
2024-05-14 13:07:09 +00:00
if err != nil {
2024-05-14 13:07:09 +00:00
return err
2024-05-14 13:07:09 +00:00
}
if minimum != nil {
2024-05-14 13:07:09 +00:00
field.minimum = minimum
2024-05-14 13:07:09 +00:00
}
multipleOf, err := getFloatTag(ps.tag, multipleOfTag)
2024-05-14 13:07:09 +00:00
if err != nil {
2024-05-14 13:07:09 +00:00
return err
2024-05-14 13:07:09 +00:00
}
if multipleOf != nil {
2024-05-14 13:07:09 +00:00
field.multipleOf = multipleOf
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
}
if field.schemaType == STRING || field.arrayType == STRING {
2024-05-14 13:07:09 +00:00
maxLength, err := getIntTag(ps.tag, maxLengthTag)
2024-05-14 13:07:09 +00:00
if err != nil {
2024-05-14 13:07:09 +00:00
return err
2024-05-14 13:07:09 +00:00
}
if maxLength != nil {
2024-05-14 13:07:09 +00:00
field.maxLength = maxLength
2024-05-14 13:07:09 +00:00
}
minLength, err := getIntTag(ps.tag, minLengthTag)
2024-05-14 13:07:09 +00:00
if err != nil {
2024-05-14 13:07:09 +00:00
return err
2024-05-14 13:07:09 +00:00
}
if minLength != nil {
2024-05-14 13:07:09 +00:00
field.minLength = minLength
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
}
// json:"name,string" or json:",string"
2024-05-14 13:07:09 +00:00
exampleTagValue, ok := ps.tag.Lookup(exampleTag)
2024-05-14 13:07:09 +00:00
if ok {
2024-05-14 13:07:09 +00:00
field.exampleValue = exampleTagValue
if !strings.Contains(jsonTagValue, ",string") {
2024-05-14 13:07:09 +00:00
example, err := defineTypeOfExample(field.schemaType, field.arrayType, exampleTagValue)
2024-05-14 13:07:09 +00:00
if err != nil {
2024-05-14 13:07:09 +00:00
return err
2024-05-14 13:07:09 +00:00
}
field.exampleValue = example
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
}
// perform this after setting everything else (min, max, etc...)
2024-05-14 13:07:09 +00:00
if strings.Contains(jsonTagValue, ",string") {
2024-05-14 13:07:09 +00:00
// @encoding/json: "It applies only to fields of string, floating point, integer, or boolean types."
2024-05-14 13:07:09 +00:00
defaultValues := map[string]string{
2024-05-14 13:07:09 +00:00
// Zero Values as string
STRING: "",
2024-05-14 13:07:09 +00:00
INTEGER: "0",
2024-05-14 13:07:09 +00:00
BOOLEAN: "false",
NUMBER: "0",
2024-05-14 13:07:09 +00:00
}
defaultValue, ok := defaultValues[field.schemaType]
2024-05-14 13:07:09 +00:00
if ok {
2024-05-14 13:07:09 +00:00
field.schemaType = STRING
2024-05-14 13:07:09 +00:00
*schema = *PrimitiveSchema(field.schemaType)
if field.exampleValue == nil {
2024-05-14 13:07:09 +00:00
// if exampleValue is not defined by the user,
2024-05-14 13:07:09 +00:00
// we will force an example with a correct value
2024-05-14 13:07:09 +00:00
// (eg: int->"0", bool:"false")
2024-05-14 13:07:09 +00:00
field.exampleValue = defaultValue
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
}
if ps.field.Doc != nil {
2024-05-14 13:07:09 +00:00
schema.Description = strings.TrimSpace(ps.field.Doc.Text())
2024-05-14 13:07:09 +00:00
}
if schema.Description == "" && ps.field.Comment != nil {
2024-05-14 13:07:09 +00:00
schema.Description = strings.TrimSpace(ps.field.Comment.Text())
2024-05-14 13:07:09 +00:00
}
schema.ReadOnly = ps.tag.Get(readOnlyTag) == "true"
defaultTagValue := ps.tag.Get(defaultTag)
2024-05-14 13:07:09 +00:00
if defaultTagValue != "" {
2024-05-14 13:07:09 +00:00
value, err := defineType(field.schemaType, defaultTagValue)
2024-05-14 13:07:09 +00:00
if err != nil {
2024-05-14 13:07:09 +00:00
return err
2024-05-14 13:07:09 +00:00
}
schema.Default = value
2024-05-14 13:07:09 +00:00
}
schema.Example = field.exampleValue
if field.schemaType != ARRAY {
2024-05-14 13:07:09 +00:00
schema.Format = field.formatType
2024-05-14 13:07:09 +00:00
}
extensionsTagValue := ps.tag.Get(extensionsTag)
2024-05-14 13:07:09 +00:00
if extensionsTagValue != "" {
2024-05-14 13:07:09 +00:00
schema.Extensions = setExtensionParam(extensionsTagValue)
2024-05-14 13:07:09 +00:00
}
varNamesTag := ps.tag.Get("x-enum-varnames")
2024-05-14 13:07:09 +00:00
if varNamesTag != "" {
2024-05-14 13:07:09 +00:00
varNames := strings.Split(varNamesTag, ",")
2024-05-14 13:07:09 +00:00
if len(varNames) != len(field.enums) {
2024-05-14 13:07:09 +00:00
return fmt.Errorf("invalid count of x-enum-varnames. expected %d, got %d", len(field.enums), len(varNames))
2024-05-14 13:07:09 +00:00
}
field.enumVarNames = nil
for _, v := range varNames {
2024-05-14 13:07:09 +00:00
field.enumVarNames = append(field.enumVarNames, v)
2024-05-14 13:07:09 +00:00
}
if field.schemaType == ARRAY {
2024-05-14 13:07:09 +00:00
// Add the var names in the items schema
2024-05-14 13:07:09 +00:00
if schema.Items.Schema.Extensions == nil {
2024-05-14 13:07:09 +00:00
schema.Items.Schema.Extensions = map[string]interface{}{}
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
schema.Items.Schema.Extensions[enumVarNamesExtension] = field.enumVarNames
2024-05-14 13:07:09 +00:00
} else {
2024-05-14 13:07:09 +00:00
// Add to top level schema
2024-05-14 13:07:09 +00:00
if schema.Extensions == nil {
2024-05-14 13:07:09 +00:00
schema.Extensions = map[string]interface{}{}
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
schema.Extensions[enumVarNamesExtension] = field.enumVarNames
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
}
eleSchema := schema
if field.schemaType == ARRAY {
2024-05-14 13:07:09 +00:00
// For Array only
2024-05-14 13:07:09 +00:00
schema.MaxItems = field.maxItems
2024-05-14 13:07:09 +00:00
schema.MinItems = field.minItems
2024-05-14 13:07:09 +00:00
schema.UniqueItems = field.unique
eleSchema = schema.Items.Schema
2024-05-14 13:07:09 +00:00
eleSchema.Format = field.formatType
2024-05-14 13:07:09 +00:00
}
eleSchema.Maximum = field.maximum
2024-05-14 13:07:09 +00:00
eleSchema.Minimum = field.minimum
2024-05-14 13:07:09 +00:00
eleSchema.MultipleOf = field.multipleOf
2024-05-14 13:07:09 +00:00
eleSchema.MaxLength = field.maxLength
2024-05-14 13:07:09 +00:00
eleSchema.MinLength = field.minLength
2024-05-14 13:07:09 +00:00
eleSchema.Enum = field.enums
return nil
2024-05-14 13:07:09 +00:00
}
func getFloatTag(structTag reflect.StructTag, tagName string) (*float64, error) {
2024-05-14 13:07:09 +00:00
strValue := structTag.Get(tagName)
2024-05-14 13:07:09 +00:00
if strValue == "" {
2024-05-14 13:07:09 +00:00
return nil, nil
2024-05-14 13:07:09 +00:00
}
value, err := strconv.ParseFloat(strValue, 64)
2024-05-14 13:07:09 +00:00
if err != nil {
2024-05-14 13:07:09 +00:00
return nil, fmt.Errorf("can't parse numeric value of %q tag: %v", tagName, err)
2024-05-14 13:07:09 +00:00
}
return &value, nil
2024-05-14 13:07:09 +00:00
}
func getIntTag(structTag reflect.StructTag, tagName string) (*int64, error) {
2024-05-14 13:07:09 +00:00
strValue := structTag.Get(tagName)
2024-05-14 13:07:09 +00:00
if strValue == "" {
2024-05-14 13:07:09 +00:00
return nil, nil
2024-05-14 13:07:09 +00:00
}
value, err := strconv.ParseInt(strValue, 10, 64)
2024-05-14 13:07:09 +00:00
if err != nil {
2024-05-14 13:07:09 +00:00
return nil, fmt.Errorf("can't parse numeric value of %q tag: %v", tagName, err)
2024-05-14 13:07:09 +00:00
}
return &value, nil
2024-05-14 13:07:09 +00:00
}
func (ps *tagBaseFieldParser) IsRequired() (bool, error) {
2024-05-14 13:07:09 +00:00
if ps.field.Tag == nil {
2024-05-14 13:07:09 +00:00
return false, nil
2024-05-14 13:07:09 +00:00
}
bindingTag := ps.tag.Get(bindingTag)
2024-05-14 13:07:09 +00:00
if bindingTag != "" {
2024-05-14 13:07:09 +00:00
for _, val := range strings.Split(bindingTag, ",") {
2024-05-14 13:07:09 +00:00
switch val {
2024-05-14 13:07:09 +00:00
case requiredLabel:
2024-05-14 13:07:09 +00:00
return true, nil
2024-05-14 13:07:09 +00:00
case optionalLabel:
2024-05-14 13:07:09 +00:00
return false, nil
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
}
validateTag := ps.tag.Get(validateTag)
2024-05-14 13:07:09 +00:00
if validateTag != "" {
2024-05-14 13:07:09 +00:00
for _, val := range strings.Split(validateTag, ",") {
2024-05-14 13:07:09 +00:00
switch val {
2024-05-14 13:07:09 +00:00
case requiredLabel:
2024-05-14 13:07:09 +00:00
return true, nil
2024-05-14 13:07:09 +00:00
case optionalLabel:
2024-05-14 13:07:09 +00:00
return false, nil
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
}
return ps.p.RequiredByDefault, nil
2024-05-14 13:07:09 +00:00
}
func parseValidTags(validTag string, sf *structField) {
2024-05-14 13:07:09 +00:00
// `validate:"required,max=10,min=1"`
2024-05-14 13:07:09 +00:00
// ps. required checked by IsRequired().
2024-05-14 13:07:09 +00:00
for _, val := range strings.Split(validTag, ",") {
2024-05-14 13:07:09 +00:00
var (
valValue string
keyVal = strings.Split(val, "=")
2024-05-14 13:07:09 +00:00
)
switch len(keyVal) {
2024-05-14 13:07:09 +00:00
case 1:
2024-05-14 13:07:09 +00:00
case 2:
2024-05-14 13:07:09 +00:00
valValue = strings.ReplaceAll(strings.ReplaceAll(keyVal[1], utf8HexComma, ","), utf8Pipe, "|")
2024-05-14 13:07:09 +00:00
default:
2024-05-14 13:07:09 +00:00
continue
2024-05-14 13:07:09 +00:00
}
switch keyVal[0] {
2024-05-14 13:07:09 +00:00
case "max", "lte":
2024-05-14 13:07:09 +00:00
sf.setMax(valValue)
2024-05-14 13:07:09 +00:00
case "min", "gte":
2024-05-14 13:07:09 +00:00
sf.setMin(valValue)
2024-05-14 13:07:09 +00:00
case "oneof":
2024-05-14 13:07:09 +00:00
sf.setOneOf(valValue)
2024-05-14 13:07:09 +00:00
case "unique":
2024-05-14 13:07:09 +00:00
if sf.schemaType == ARRAY {
2024-05-14 13:07:09 +00:00
sf.unique = true
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
case "dive":
2024-05-14 13:07:09 +00:00
// ignore dive
2024-05-14 13:07:09 +00:00
return
2024-05-14 13:07:09 +00:00
default:
2024-05-14 13:07:09 +00:00
continue
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
}
func parseEnumTags(enumTag string, field *structField) error {
2024-05-14 13:07:09 +00:00
enumType := field.schemaType
2024-05-14 13:07:09 +00:00
if field.schemaType == ARRAY {
2024-05-14 13:07:09 +00:00
enumType = field.arrayType
2024-05-14 13:07:09 +00:00
}
field.enums = nil
for _, e := range strings.Split(enumTag, ",") {
2024-05-14 13:07:09 +00:00
value, err := defineType(enumType, e)
2024-05-14 13:07:09 +00:00
if err != nil {
2024-05-14 13:07:09 +00:00
return err
2024-05-14 13:07:09 +00:00
}
field.enums = append(field.enums, value)
2024-05-14 13:07:09 +00:00
}
return nil
2024-05-14 13:07:09 +00:00
}
func (sf *structField) setOneOf(valValue string) {
2024-05-14 13:07:09 +00:00
if len(sf.enums) != 0 {
2024-05-14 13:07:09 +00:00
return
2024-05-14 13:07:09 +00:00
}
enumType := sf.schemaType
2024-05-14 13:07:09 +00:00
if sf.schemaType == ARRAY {
2024-05-14 13:07:09 +00:00
enumType = sf.arrayType
2024-05-14 13:07:09 +00:00
}
valValues := parseOneOfParam2(valValue)
2024-05-14 13:07:09 +00:00
for i := range valValues {
2024-05-14 13:07:09 +00:00
value, err := defineType(enumType, valValues[i])
2024-05-14 13:07:09 +00:00
if err != nil {
2024-05-14 13:07:09 +00:00
continue
2024-05-14 13:07:09 +00:00
}
sf.enums = append(sf.enums, value)
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
}
func (sf *structField) setMin(valValue string) {
2024-05-14 13:07:09 +00:00
value, err := strconv.ParseFloat(valValue, 64)
2024-05-14 13:07:09 +00:00
if err != nil {
2024-05-14 13:07:09 +00:00
return
2024-05-14 13:07:09 +00:00
}
switch sf.schemaType {
2024-05-14 13:07:09 +00:00
case INTEGER, NUMBER:
2024-05-14 13:07:09 +00:00
sf.minimum = &value
2024-05-14 13:07:09 +00:00
case STRING:
2024-05-14 13:07:09 +00:00
intValue := int64(value)
2024-05-14 13:07:09 +00:00
sf.minLength = &intValue
2024-05-14 13:07:09 +00:00
case ARRAY:
2024-05-14 13:07:09 +00:00
intValue := int64(value)
2024-05-14 13:07:09 +00:00
sf.minItems = &intValue
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
}
func (sf *structField) setMax(valValue string) {
2024-05-14 13:07:09 +00:00
value, err := strconv.ParseFloat(valValue, 64)
2024-05-14 13:07:09 +00:00
if err != nil {
2024-05-14 13:07:09 +00:00
return
2024-05-14 13:07:09 +00:00
}
switch sf.schemaType {
2024-05-14 13:07:09 +00:00
case INTEGER, NUMBER:
2024-05-14 13:07:09 +00:00
sf.maximum = &value
2024-05-14 13:07:09 +00:00
case STRING:
2024-05-14 13:07:09 +00:00
intValue := int64(value)
2024-05-14 13:07:09 +00:00
sf.maxLength = &intValue
2024-05-14 13:07:09 +00:00
case ARRAY:
2024-05-14 13:07:09 +00:00
intValue := int64(value)
2024-05-14 13:07:09 +00:00
sf.maxItems = &intValue
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
}
const (
utf8HexComma = "0x2C"
utf8Pipe = "0x7C"
2024-05-14 13:07:09 +00:00
)
// These code copy from
2024-05-14 13:07:09 +00:00
// https://github.com/go-playground/validator/blob/d4271985b44b735c6f76abc7a06532ee997f9476/baked_in.go#L207
2024-05-14 13:07:09 +00:00
// ---.
2024-05-14 13:07:09 +00:00
var oneofValsCache = map[string][]string{}
2024-05-14 13:07:09 +00:00
var oneofValsCacheRWLock = sync.RWMutex{}
2024-05-14 13:07:09 +00:00
var splitParamsRegex = regexp.MustCompile(`'[^']*'|\S+`)
func parseOneOfParam2(param string) []string {
2024-05-14 13:07:09 +00:00
oneofValsCacheRWLock.RLock()
2024-05-14 13:07:09 +00:00
values, ok := oneofValsCache[param]
2024-05-14 13:07:09 +00:00
oneofValsCacheRWLock.RUnlock()
if !ok {
2024-05-14 13:07:09 +00:00
oneofValsCacheRWLock.Lock()
2024-05-14 13:07:09 +00:00
values = splitParamsRegex.FindAllString(param, -1)
for i := 0; i < len(values); i++ {
2024-05-14 13:07:09 +00:00
values[i] = strings.ReplaceAll(values[i], "'", "")
2024-05-14 13:07:09 +00:00
}
oneofValsCache[param] = values
oneofValsCacheRWLock.Unlock()
2024-05-14 13:07:09 +00:00
}
return values
2024-05-14 13:07:09 +00:00
}
// ---.