From e50db3e95f8cda4430c3222dd03898b698227fd2 Mon Sep 17 00:00:00 2001 From: sdghchj Date: Wed, 30 Nov 2022 09:34:40 +0800 Subject: [PATCH] enhancement for enums (#1400) * enhancement for enums: 1. explicit enum conversion; 2. implicit integer conversion; 3. keep the type of the right operand of a shift operation; 3. parse escape characters. --- const.go | 551 +++++++++++++++++++++++++++++++++ enums_test.go | 9 + package.go | 115 ++++--- testdata/enums/consts/const.go | 9 + testdata/enums/main.go | 1 - testdata/enums/types/model.go | 21 +- utils_go18.go | 31 ++ utils_other.go | 47 +++ 8 files changed, 731 insertions(+), 53 deletions(-) create mode 100644 utils_go18.go create mode 100644 utils_other.go diff --git a/const.go b/const.go index d9f970bb1..1353b6d1d 100644 --- a/const.go +++ b/const.go @@ -2,6 +2,10 @@ package swag import ( "go/ast" + "go/token" + "reflect" + "strconv" + "strings" ) // ConstVariable a model to record a const variable @@ -13,3 +17,550 @@ type ConstVariable struct { File *ast.File Pkg *PackageDefinitions } + +var escapedChars = map[uint8]uint8{ + 'n': '\n', + 'r': '\r', + 't': '\t', + 'v': '\v', + '\\': '\\', + '"': '"', +} + +// EvaluateEscapedChar parse escaped character +func EvaluateEscapedChar(text string) rune { + if len(text) == 1 { + return rune(text[0]) + } + + if len(text) == 2 && text[0] == '\\' { + return rune(escapedChars[text[1]]) + } + + if len(text) == 6 && text[0:2] == "\\u" { + n, err := strconv.ParseInt(text[2:], 16, 32) + if err == nil { + return rune(n) + } + } + + return 0 +} + +// EvaluateEscapedString parse escaped characters in string +func EvaluateEscapedString(text string) string { + if !strings.ContainsRune(text, '\\') { + return text + } + result := make([]byte, 0, len(text)) + for i := 0; i < len(text); i++ { + if text[i] == '\\' { + i++ + if text[i] == 'u' { + i++ + char, err := strconv.ParseInt(text[i:i+4], 16, 32) + if err == nil { + result = AppendUtf8Rune(result, rune(char)) + } + i += 3 + } else if c, ok := escapedChars[text[i]]; ok { + result = append(result, c) + } + } else { + result = append(result, text[i]) + } + } + return string(result) +} + +// EvaluateDataConversion evaluate the type a explicit type conversion +func EvaluateDataConversion(x interface{}, typeName string) interface{} { + switch value := x.(type) { + case int: + switch typeName { + case "int": + return int(value) + case "byte": + return byte(value) + case "int8": + return int8(value) + case "int16": + return int16(value) + case "int32": + return int32(value) + case "int64": + return int64(value) + case "uint": + return uint(value) + case "uint8": + return uint8(value) + case "uint16": + return uint16(value) + case "uint32": + return uint32(value) + case "uint64": + return uint64(value) + case "rune": + return rune(value) + } + case uint: + switch typeName { + case "int": + return int(value) + case "byte": + return byte(value) + case "int8": + return int8(value) + case "int16": + return int16(value) + case "int32": + return int32(value) + case "int64": + return int64(value) + case "uint": + return uint(value) + case "uint8": + return uint8(value) + case "uint16": + return uint16(value) + case "uint32": + return uint32(value) + case "uint64": + return uint64(value) + case "rune": + return rune(value) + } + case int8: + switch typeName { + case "int": + return int(value) + case "byte": + return byte(value) + case "int8": + return int8(value) + case "int16": + return int16(value) + case "int32": + return int32(value) + case "int64": + return int64(value) + case "uint": + return uint(value) + case "uint8": + return uint8(value) + case "uint16": + return uint16(value) + case "uint32": + return uint32(value) + case "uint64": + return uint64(value) + case "rune": + return rune(value) + } + case uint8: + switch typeName { + case "int": + return int(value) + case "byte": + return byte(value) + case "int8": + return int8(value) + case "int16": + return int16(value) + case "int32": + return int32(value) + case "int64": + return int64(value) + case "uint": + return uint(value) + case "uint8": + return uint8(value) + case "uint16": + return uint16(value) + case "uint32": + return uint32(value) + case "uint64": + return uint64(value) + case "rune": + return rune(value) + } + case int16: + switch typeName { + case "int": + return int(value) + case "byte": + return byte(value) + case "int8": + return int8(value) + case "int16": + return int16(value) + case "int32": + return int32(value) + case "int64": + return int64(value) + case "uint": + return uint(value) + case "uint8": + return uint8(value) + case "uint16": + return uint16(value) + case "uint32": + return uint32(value) + case "uint64": + return uint64(value) + case "rune": + return rune(value) + } + case uint16: + switch typeName { + case "int": + return int(value) + case "byte": + return byte(value) + case "int8": + return int8(value) + case "int16": + return int16(value) + case "int32": + return int32(value) + case "int64": + return int64(value) + case "uint": + return uint(value) + case "uint8": + return uint8(value) + case "uint16": + return uint16(value) + case "uint32": + return uint32(value) + case "uint64": + return uint64(value) + case "rune": + return rune(value) + } + case int32: + switch typeName { + case "int": + return int(value) + case "byte": + return byte(value) + case "int8": + return int8(value) + case "int16": + return int16(value) + case "int32": + return int32(value) + case "int64": + return int64(value) + case "uint": + return uint(value) + case "uint8": + return uint8(value) + case "uint16": + return uint16(value) + case "uint32": + return uint32(value) + case "uint64": + return uint64(value) + case "rune": + return rune(value) + case "string": + return string(value) + } + case uint32: + switch typeName { + case "int": + return int(value) + case "byte": + return byte(value) + case "int8": + return int8(value) + case "int16": + return int16(value) + case "int32": + return int32(value) + case "int64": + return int64(value) + case "uint": + return uint(value) + case "uint8": + return uint8(value) + case "uint16": + return uint16(value) + case "uint32": + return uint32(value) + case "uint64": + return uint64(value) + case "rune": + return rune(value) + } + case int64: + switch typeName { + case "int": + return int(value) + case "byte": + return byte(value) + case "int8": + return int8(value) + case "int16": + return int16(value) + case "int32": + return int32(value) + case "int64": + return int64(value) + case "uint": + return uint(value) + case "uint8": + return uint8(value) + case "uint16": + return uint16(value) + case "uint32": + return uint32(value) + case "uint64": + return uint64(value) + case "rune": + return rune(value) + } + case uint64: + switch typeName { + case "int": + return int(value) + case "byte": + return byte(value) + case "int8": + return int8(value) + case "int16": + return int16(value) + case "int32": + return int32(value) + case "int64": + return int64(value) + case "uint": + return uint(value) + case "uint8": + return uint8(value) + case "uint16": + return uint16(value) + case "uint32": + return uint32(value) + case "uint64": + return uint64(value) + case "rune": + return rune(value) + } + case string: + switch typeName { + case "string": + return value + } + } + return nil +} + +// EvaluateUnary evaluate the type and value of a unary expression +func EvaluateUnary(x interface{}, operator token.Token, xtype ast.Expr) (interface{}, ast.Expr) { + switch operator { + case token.SUB: + switch value := x.(type) { + case int: + return -value, xtype + case int8: + return -value, xtype + case int16: + return -value, xtype + case int32: + return -value, xtype + case int64: + return -value, xtype + } + case token.XOR: + switch value := x.(type) { + case int: + return ^value, xtype + case int8: + return ^value, xtype + case int16: + return ^value, xtype + case int32: + return ^value, xtype + case int64: + return ^value, xtype + case uint: + return ^value, xtype + case uint8: + return ^value, xtype + case uint16: + return ^value, xtype + case uint32: + return ^value, xtype + case uint64: + return ^value, xtype + } + } + return nil, nil +} + +// EvaluateBinary evaluate the type and value of a binary expression +func EvaluateBinary(x, y interface{}, operator token.Token, xtype, ytype ast.Expr) (interface{}, ast.Expr) { + if operator == token.SHR || operator == token.SHL { + var rightOperand uint64 + yValue := CanIntegerValue{reflect.ValueOf(y)} + if yValue.CanUint() { + rightOperand = yValue.Uint() + } else if yValue.CanInt() { + rightOperand = uint64(yValue.Int()) + } + + switch operator { + case token.SHL: + switch xValue := x.(type) { + case int: + return xValue << rightOperand, xtype + case int8: + return xValue << rightOperand, xtype + case int16: + return xValue << rightOperand, xtype + case int32: + return xValue << rightOperand, xtype + case int64: + return xValue << rightOperand, xtype + case uint: + return xValue << rightOperand, xtype + case uint8: + return xValue << rightOperand, xtype + case uint16: + return xValue << rightOperand, xtype + case uint32: + return xValue << rightOperand, xtype + case uint64: + return xValue << rightOperand, xtype + } + case token.SHR: + switch xValue := x.(type) { + case int: + return xValue >> rightOperand, xtype + case int8: + return xValue >> rightOperand, xtype + case int16: + return xValue >> rightOperand, xtype + case int32: + return xValue >> rightOperand, xtype + case int64: + return xValue >> rightOperand, xtype + case uint: + return xValue >> rightOperand, xtype + case uint8: + return xValue >> rightOperand, xtype + case uint16: + return xValue >> rightOperand, xtype + case uint32: + return xValue >> rightOperand, xtype + case uint64: + return xValue >> rightOperand, xtype + } + } + return nil, nil + } + + evalType := xtype + if evalType == nil { + evalType = ytype + } + + xValue := CanIntegerValue{reflect.ValueOf(x)} + yValue := CanIntegerValue{reflect.ValueOf(y)} + if xValue.Kind() == reflect.String && yValue.Kind() == reflect.String { + return xValue.String() + yValue.String(), evalType + } + + var targetValue reflect.Value + if xValue.Kind() != reflect.Int { + targetValue = reflect.New(xValue.Type()).Elem() + } else { + targetValue = reflect.New(yValue.Type()).Elem() + } + + switch operator { + case token.ADD: + if xValue.CanInt() && yValue.CanInt() { + targetValue.SetInt(xValue.Int() + yValue.Int()) + } else if xValue.CanUint() && yValue.CanUint() { + targetValue.SetUint(xValue.Uint() + yValue.Uint()) + } else if xValue.CanInt() && yValue.CanUint() { + targetValue.SetUint(uint64(xValue.Int()) + yValue.Uint()) + } else if xValue.CanUint() && yValue.CanInt() { + targetValue.SetUint(xValue.Uint() + uint64(yValue.Int())) + } + case token.SUB: + if xValue.CanInt() && yValue.CanInt() { + targetValue.SetInt(xValue.Int() - yValue.Int()) + } else if xValue.CanUint() && yValue.CanUint() { + targetValue.SetUint(xValue.Uint() - yValue.Uint()) + } else if xValue.CanInt() && yValue.CanUint() { + targetValue.SetUint(uint64(xValue.Int()) - yValue.Uint()) + } else if xValue.CanUint() && yValue.CanInt() { + targetValue.SetUint(xValue.Uint() - uint64(yValue.Int())) + } + case token.MUL: + if xValue.CanInt() && yValue.CanInt() { + targetValue.SetInt(xValue.Int() * yValue.Int()) + } else if xValue.CanUint() && yValue.CanUint() { + targetValue.SetUint(xValue.Uint() * yValue.Uint()) + } else if xValue.CanInt() && yValue.CanUint() { + targetValue.SetUint(uint64(xValue.Int()) * yValue.Uint()) + } else if xValue.CanUint() && yValue.CanInt() { + targetValue.SetUint(xValue.Uint() * uint64(yValue.Int())) + } + case token.QUO: + if xValue.CanInt() && yValue.CanInt() { + targetValue.SetInt(xValue.Int() / yValue.Int()) + } else if xValue.CanUint() && yValue.CanUint() { + targetValue.SetUint(xValue.Uint() / yValue.Uint()) + } else if xValue.CanInt() && yValue.CanUint() { + targetValue.SetUint(uint64(xValue.Int()) / yValue.Uint()) + } else if xValue.CanUint() && yValue.CanInt() { + targetValue.SetUint(xValue.Uint() / uint64(yValue.Int())) + } + case token.REM: + if xValue.CanInt() && yValue.CanInt() { + targetValue.SetInt(xValue.Int() % yValue.Int()) + } else if xValue.CanUint() && yValue.CanUint() { + targetValue.SetUint(xValue.Uint() % yValue.Uint()) + } else if xValue.CanInt() && yValue.CanUint() { + targetValue.SetUint(uint64(xValue.Int()) % yValue.Uint()) + } else if xValue.CanUint() && yValue.CanInt() { + targetValue.SetUint(xValue.Uint() % uint64(yValue.Int())) + } + case token.AND: + if xValue.CanInt() && yValue.CanInt() { + targetValue.SetInt(xValue.Int() & yValue.Int()) + } else if xValue.CanUint() && yValue.CanUint() { + targetValue.SetUint(xValue.Uint() & yValue.Uint()) + } else if xValue.CanInt() && yValue.CanUint() { + targetValue.SetUint(uint64(xValue.Int()) & yValue.Uint()) + } else if xValue.CanUint() && yValue.CanInt() { + targetValue.SetUint(xValue.Uint() & uint64(yValue.Int())) + } + case token.OR: + if xValue.CanInt() && yValue.CanInt() { + targetValue.SetInt(xValue.Int() | yValue.Int()) + } else if xValue.CanUint() && yValue.CanUint() { + targetValue.SetUint(xValue.Uint() | yValue.Uint()) + } else if xValue.CanInt() && yValue.CanUint() { + targetValue.SetUint(uint64(xValue.Int()) | yValue.Uint()) + } else if xValue.CanUint() && yValue.CanInt() { + targetValue.SetUint(xValue.Uint() | uint64(yValue.Int())) + } + case token.XOR: + if xValue.CanInt() && yValue.CanInt() { + targetValue.SetInt(xValue.Int() ^ yValue.Int()) + } else if xValue.CanUint() && yValue.CanUint() { + targetValue.SetUint(xValue.Uint() ^ yValue.Uint()) + } else if xValue.CanInt() && yValue.CanUint() { + targetValue.SetUint(uint64(xValue.Int()) ^ yValue.Uint()) + } else if xValue.CanUint() && yValue.CanInt() { + targetValue.SetUint(xValue.Uint() ^ uint64(yValue.Int())) + } + } + return targetValue.Interface(), evalType +} diff --git a/enums_test.go b/enums_test.go index b77f1d227..dbae10d83 100644 --- a/enums_test.go +++ b/enums_test.go @@ -20,4 +20,13 @@ func TestParseGlobalEnums(t *testing.T) { b, err := json.MarshalIndent(p.swagger, "", " ") assert.NoError(t, err) assert.Equal(t, string(expected), string(b)) + constsPath := "github.com/swaggo/swag/testdata/enums/consts" + assert.Equal(t, 64, p.packages.packages[constsPath].ConstTable["uintSize"].Value) + assert.Equal(t, int32(62), p.packages.packages[constsPath].ConstTable["maxBase"].Value) + assert.Equal(t, 8, p.packages.packages[constsPath].ConstTable["shlByLen"].Value) + assert.Equal(t, 255, p.packages.packages[constsPath].ConstTable["hexnum"].Value) + assert.Equal(t, 15, p.packages.packages[constsPath].ConstTable["octnum"].Value) + assert.Equal(t, `aa\nbb\u8888cc`, p.packages.packages[constsPath].ConstTable["nonescapestr"].Value) + assert.Equal(t, "aa\nbb\u8888cc", p.packages.packages[constsPath].ConstTable["escapestr"].Value) + assert.Equal(t, '\u8888', p.packages.packages[constsPath].ConstTable["escapechar"].Value) } diff --git a/package.go b/package.go index bcce3f65c..08f8b92b3 100644 --- a/package.go +++ b/package.go @@ -3,6 +3,7 @@ package swag import ( "go/ast" "go/token" + "reflect" "strconv" ) @@ -31,6 +32,7 @@ type PackageDefinitions struct { type ConstVariableGlobalEvaluator interface { EvaluateConstValue(pkg *PackageDefinitions, cv *ConstVariable, recursiveStack map[string]struct{}) (interface{}, ast.Expr) EvaluateConstValueByName(file *ast.File, pkgPath, constVariableName string, recursiveStack map[string]struct{}) (interface{}, ast.Expr) + FindTypeSpec(typeName string, file *ast.File) *TypeSpecDef } // NewPackageDefinitions new a PackageDefinitions object @@ -92,68 +94,89 @@ func (pkg *PackageDefinitions) evaluateConstValue(file *ast.File, iota int, expr case *ast.BasicLit: switch valueExpr.Kind { case token.INT: - x, err := strconv.ParseInt(valueExpr.Value, 10, 64) - if err != nil { - return nil, nil + // hexadecimal + if len(valueExpr.Value) > 2 && valueExpr.Value[0] == '0' && valueExpr.Value[1] == 'x' { + if x, err := strconv.ParseInt(valueExpr.Value[2:], 16, 64); err == nil { + return int(x), nil + } else if x, err := strconv.ParseUint(valueExpr.Value[2:], 16, 64); err == nil { + return x, nil + } else { + panic(err) + } + } + + //octet + if len(valueExpr.Value) > 1 && valueExpr.Value[0] == '0' { + if x, err := strconv.ParseInt(valueExpr.Value[1:], 8, 64); err == nil { + return int(x), nil + } else if x, err := strconv.ParseUint(valueExpr.Value[1:], 8, 64); err == nil { + return x, nil + } else { + panic(err) + } + } + + //a basic literal integer is int type in default, or must have an explicit converting type in front + if x, err := strconv.ParseInt(valueExpr.Value, 10, 64); err == nil { + return int(x), nil + } else if x, err := strconv.ParseUint(valueExpr.Value, 10, 64); err == nil { + return x, nil + } else { + panic(err) } - return int(x), nil - case token.STRING, token.CHAR: - return valueExpr.Value[1 : len(valueExpr.Value)-1], nil + case token.STRING: + if valueExpr.Value[0] == '`' { + return valueExpr.Value[1 : len(valueExpr.Value)-1], nil + } + return EvaluateEscapedString(valueExpr.Value[1 : len(valueExpr.Value)-1]), nil + case token.CHAR: + return EvaluateEscapedChar(valueExpr.Value[1 : len(valueExpr.Value)-1]), nil } case *ast.UnaryExpr: x, evalType := pkg.evaluateConstValue(file, iota, valueExpr.X, globalEvaluator, recursiveStack) if x == nil { - return nil, nil - } - switch valueExpr.Op { - case token.SUB: - return -x.(int), evalType - case token.XOR: - return ^(x.(int)), evalType + return x, evalType } + return EvaluateUnary(x, valueExpr.Op, evalType) case *ast.BinaryExpr: x, evalTypex := pkg.evaluateConstValue(file, iota, valueExpr.X, globalEvaluator, recursiveStack) y, evalTypey := pkg.evaluateConstValue(file, iota, valueExpr.Y, globalEvaluator, recursiveStack) if x == nil || y == nil { return nil, nil } - evalType := evalTypex - if evalType == nil { - evalType = evalTypey - } - switch valueExpr.Op { - case token.ADD: - if ix, ok := x.(int); ok { - return ix + y.(int), evalType - } else if sx, ok := x.(string); ok { - return sx + y.(string), evalType - } - case token.SUB: - return x.(int) - y.(int), evalType - case token.MUL: - return x.(int) * y.(int), evalType - case token.QUO: - return x.(int) / y.(int), evalType - case token.REM: - return x.(int) % y.(int), evalType - case token.AND: - return x.(int) & y.(int), evalType - case token.OR: - return x.(int) | y.(int), evalType - case token.XOR: - return x.(int) ^ y.(int), evalType - case token.SHL: - return x.(int) << y.(int), evalType - case token.SHR: - return x.(int) >> y.(int), evalType - } + return EvaluateBinary(x, y, valueExpr.Op, evalTypex, evalTypey) case *ast.ParenExpr: return pkg.evaluateConstValue(file, iota, valueExpr.X, globalEvaluator, recursiveStack) case *ast.CallExpr: //data conversion - if ident, ok := valueExpr.Fun.(*ast.Ident); ok && len(valueExpr.Args) == 1 && IsGolangPrimitiveType(ident.Name) { - arg, _ := pkg.evaluateConstValue(file, iota, valueExpr.Args[0], globalEvaluator, recursiveStack) - return arg, nil + if len(valueExpr.Args) != 1 { + return nil, nil + } + arg := valueExpr.Args[0] + if ident, ok := valueExpr.Fun.(*ast.Ident); ok { + name := ident.Name + if name == "uintptr" { + name = "uint" + } + if IsGolangPrimitiveType(name) { + value, _ := pkg.evaluateConstValue(file, iota, arg, globalEvaluator, recursiveStack) + value = EvaluateDataConversion(value, name) + return value, nil + } else if name == "len" { + value, _ := pkg.evaluateConstValue(file, iota, arg, globalEvaluator, recursiveStack) + return reflect.ValueOf(value).Len(), nil + } + typeDef := globalEvaluator.FindTypeSpec(name, file) + if typeDef == nil { + return nil, nil + } + return arg, valueExpr.Fun + } else if selector, ok := valueExpr.Fun.(*ast.SelectorExpr); ok { + typeDef := globalEvaluator.FindTypeSpec(fullTypeName(selector.X.(*ast.Ident).Name, selector.Sel.Name), file) + if typeDef == nil { + return nil, nil + } + return arg, typeDef.TypeSpec.Type } } return nil, nil diff --git a/testdata/enums/consts/const.go b/testdata/enums/consts/const.go index db3ff311e..83dc97fa7 100644 --- a/testdata/enums/consts/const.go +++ b/testdata/enums/consts/const.go @@ -1,3 +1,12 @@ package consts const Base = 1 + +const uintSize = 32 << (^uint(uintptr(0)) >> 63) +const maxBase = 10 + ('z' - 'a' + 1) + ('Z' - 'A' + 1) +const shlByLen = 1 << len("aaa") +const hexnum = 0xFF +const octnum = 017 +const nonescapestr = `aa\nbb\u8888cc` +const escapestr = "aa\nbb\u8888cc" +const escapechar = '\u8888' diff --git a/testdata/enums/main.go b/testdata/enums/main.go index 8238df86d..b3c29b55d 100644 --- a/testdata/enums/main.go +++ b/testdata/enums/main.go @@ -14,5 +14,4 @@ package main // @BasePath /v2 func main() { - } diff --git a/testdata/enums/types/model.go b/testdata/enums/types/model.go index 79a4e83f3..8438a8017 100644 --- a/testdata/enums/types/model.go +++ b/testdata/enums/types/model.go @@ -11,8 +11,8 @@ const ( A Class = consts.Base + (iota+1-1)*2/2%100 - (1&1 | 1) + (2 ^ 2) // AAA B /* BBB */ C - D - F = D + 1 + D = C + 1 + F = Class(5) //G is not enum G = H + 10 //H is not enum @@ -21,13 +21,15 @@ const ( I = int(F + 2) ) +const J = 1 << uint16(I) + type Mask int const ( - Mask1 Mask = 2 << iota >> 1 // Mask1 - Mask2 /* Mask2 */ - Mask3 // Mask3 - Mask4 // Mask4 + Mask1 Mask = 0x02 << iota >> 1 // Mask1 + Mask2 /* Mask2 */ + Mask3 // Mask3 + Mask4 // Mask4 ) type Type string @@ -40,6 +42,13 @@ const ( OtherUnknown = string(Other + Unknown) ) +type Sex rune + +const ( + Male Sex = 'M' + Female = 'F' +) + type Person struct { Name string Class Class diff --git a/utils_go18.go b/utils_go18.go new file mode 100644 index 000000000..814f93433 --- /dev/null +++ b/utils_go18.go @@ -0,0 +1,31 @@ +//go:build go1.18 +// +build go1.18 + +package swag + +import ( + "reflect" + "unicode/utf8" +) + +// AppendUtf8Rune appends the UTF-8 encoding of r to the end of p and +// returns the extended buffer. If the rune is out of range, +// it appends the encoding of RuneError. +func AppendUtf8Rune(p []byte, r rune) []byte { + return utf8.AppendRune(p, r) +} + +// CanIntegerValue a wrapper of reflect.Value +type CanIntegerValue struct { + reflect.Value +} + +// CanInt reports whether Uint can be used without panicking. +func (v CanIntegerValue) CanInt() bool { + return v.Value.CanInt() +} + +// CanUint reports whether Uint can be used without panicking. +func (v CanIntegerValue) CanUint() bool { + return v.Value.CanUint() +} diff --git a/utils_other.go b/utils_other.go new file mode 100644 index 000000000..531c0df12 --- /dev/null +++ b/utils_other.go @@ -0,0 +1,47 @@ +//go:build !go1.18 +// +build !go1.18 + +package swag + +import ( + "reflect" + "unicode/utf8" +) + +// AppendUtf8Rune appends the UTF-8 encoding of r to the end of p and +// returns the extended buffer. If the rune is out of range, +// it appends the encoding of RuneError. +func AppendUtf8Rune(p []byte, r rune) []byte { + length := utf8.RuneLen(rune(r)) + if length > 0 { + utf8Slice := make([]byte, length) + utf8.EncodeRune(utf8Slice, rune(r)) + p = append(p, utf8Slice...) + } + return p +} + +// CanIntegerValue a wrapper of reflect.Value +type CanIntegerValue struct { + reflect.Value +} + +// CanInt reports whether Uint can be used without panicking. +func (v CanIntegerValue) CanInt() bool { + switch v.Kind() { + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return true + default: + return false + } +} + +// CanUint reports whether Uint can be used without panicking. +func (v CanIntegerValue) CanUint() bool { + switch v.Kind() { + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + return true + default: + return false + } +}