From 9b1f0c8d2b5f70fea4ec3b45f5dee937a15ff0ae Mon Sep 17 00:00:00 2001 From: zc Date: Sun, 7 Apr 2024 09:31:42 +0800 Subject: [PATCH] support map and multi slice (#43) --- README.md | 25 +-- README_zh.md | 25 +-- api.go | 47 +++--- endpoint.go | 10 +- reflect.go | 358 +++++++++--------------------------------- reflect_test.go | 48 ++---- testdata/pet.json | 17 +- types/convert.go | 62 ++++++++ types/convert_test.go | 42 +++++ types/format.go | 51 ++++++ types/format_test.go | 29 ++++ types/types.go | 9 +- types/types_test.go | 16 +- 13 files changed, 311 insertions(+), 428 deletions(-) create mode 100644 types/convert.go create mode 100644 types/convert_test.go create mode 100644 types/format.go create mode 100644 types/format_test.go diff --git a/README.md b/README.md index 18ae1c5..1b9ee4f 100644 --- a/README.md +++ b/README.md @@ -21,30 +21,7 @@ Golang 1.16+ ## Installation ```shell -go get -u github.com/zc2638/swag@v1.4.3 -``` - -## Use note: -The time.time type uses jsonTag to parse it into a string type -```go -type XXX struct{ - Now time.Time `json:",string"` -} -``` - -The compatibility is resolved as follows: -```go -type xxx struct { - MapSlicePtr map[string][]*string - MapSlice map[string][]string - MapSliceStructPtr map[string][]*Person - MapSliceStruct map[string][]Person - SliceStructPtr *[]*Person - SliceStruct *[]Person - SliceStringPtr *[]*string - SliceString *[]string -} - +go get -u github.com/zc2638/swag@v1.5.1 ``` **Tip:** As of `v1.2.0`, lower versions are no longer compatible. In order to be compatible with most web frameworks, diff --git a/README_zh.md b/README_zh.md index 0d9e048..a830a24 100644 --- a/README_zh.md +++ b/README_zh.md @@ -21,30 +21,7 @@ Golang 1.16+ ## 安装 ```shell -go get -u github.com/zc2638/swag@v1.4.3 -``` - -## 使用注意: -time.time类型使用jsonTag来将其解析为string类型 -```go -type XXX struct{ - Now time.Time `json:",string"` -} -``` - -兼容的解析如下: -```go -type xxx struct { - MapSlicePtr map[string][]*string - MapSlice map[string][]string - MapSliceStructPtr map[string][]*Person - MapSliceStruct map[string][]Person - SliceStructPtr *[]*Person - SliceStruct *[]Person - SliceStringPtr *[]*string - SliceString *[]string -} - +go get -u github.com/zc2638/swag@v1.5.1 ``` **Tip:** 从 `v1.2.0` 开始,低版本不再兼容。为了兼容大部分的web框架,整体架构做了很大的改动。 diff --git a/api.go b/api.go index fda374d..c10b115 100644 --- a/api.go +++ b/api.go @@ -29,38 +29,27 @@ import ( // Object represents the object entity from the swagger definition type Object struct { - IsArray bool `json:"-"` - GoType reflect.Type `json:"-"` - Name string `json:"-"` - Type string `json:"type"` - Description string `json:"description,omitempty"` - Format string `json:"format,omitempty"` - Required []string `json:"required,omitempty"` - Properties map[string]Property `json:"properties,omitempty"` + Name string `json:"-"` + Type string `json:"type"` + Description string `json:"description,omitempty"` + Format string `json:"format,omitempty"` + Required []string `json:"required,omitempty"` + Properties map[string]Property `json:"properties,omitempty"` + AdditionalProperties *Property `json:"additionalProperties,omitempty"` + Items *Property `json:"items,omitempty"` } // Property represents the property entity from the swagger definition type Property struct { - GoType reflect.Type `json:"-"` - Type string `json:"type,omitempty"` - Description string `json:"description,omitempty"` - Enum []string `json:"enum,omitempty"` - Format string `json:"format,omitempty"` - Ref string `json:"$ref,omitempty"` - Example string `json:"example,omitempty"` - Items *Items `json:"items,omitempty"` - AddPropertie *AdditionalProperties `json:"additionalProperties,omitempty"` -} - -type AdditionalProperties struct { - GoType reflect.Type `json:"-"` - Type string `json:"type,omitempty"` - Description string `json:"description,omitempty"` - Enum []string `json:"enum,omitempty"` - Format string `json:"format,omitempty"` - Ref string `json:"$ref,omitempty"` - Example string `json:"example,omitempty"` - Items *Items `json:"items,omitempty"` + GoType reflect.Type `json:"-"` + Type string `json:"type,omitempty"` + Description string `json:"description,omitempty"` + Enum []string `json:"enum,omitempty"` + Format string `json:"format,omitempty"` + Ref string `json:"$ref,omitempty"` + Example string `json:"example,omitempty"` + Items *Property `json:"items,omitempty"` + AdditionalProperties *Property `json:"additionalProperties,omitempty"` } // Contact represents the contact entity from the swagger definition; used by Info @@ -252,7 +241,7 @@ func (a *API) addPath(e *Endpoint) { func (a *API) addDefinition(e *Endpoint) { if a.Definitions == nil { - a.Definitions = map[string]Object{} + a.Definitions = make(map[string]Object) } if e.Parameters != nil { diff --git a/endpoint.go b/endpoint.go index 8196705..02d184e 100644 --- a/endpoint.go +++ b/endpoint.go @@ -21,17 +21,9 @@ import ( "github.com/zc2638/swag/types" ) -// Items represents items from the swagger doc -type Items struct { - Type string `json:"type,omitempty"` - Format string `json:"format,omitempty"` - Ref string `json:"$ref,omitempty"` -} - // Schema represents a schema from the swagger doc type Schema struct { - Type string `json:"type,omitempty"` - Items *Items `json:"items,omitempty"` + Items *Property `json:"items,omitempty"` Ref string `json:"$ref,omitempty"` Prototype interface{} `json:"-"` } diff --git a/reflect.go b/reflect.go index 66cebcf..6464c2c 100644 --- a/reflect.go +++ b/reflect.go @@ -15,260 +15,40 @@ package swag import ( - "github.com/zc2638/swag/types" "reflect" "strings" + + "github.com/zc2638/swag/types" ) -func inspect(t reflect.Type, jsonTag string) Property { - p := Property{ - GoType: t, +func inspect(t reflect.Type) Property { + // unwrap pointer + if t.Kind() == reflect.Ptr { + return inspect(t.Elem()) } - if strings.Contains(jsonTag, ",string") { - p.Type = "string" - return p - } - - if p.GoType.Kind() == reflect.Ptr { - p.GoType = p.GoType.Elem() - } + p := Property{GoType: t} + p.Type = types.Parse(p.GoType).String() + p.Format = types.ParseFormat(p.GoType).String() switch p.GoType.Kind() { - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Uint8, reflect.Uint16, reflect.Uint32: - p.Type = types.Integer.String() - p.Format = "int32" - - case reflect.Int64, reflect.Uint64: - p.Type = types.Integer.String() - p.Format = "int64" - - case reflect.Float64: - p.Type = types.Number.String() - p.Format = "double" - - case reflect.Float32: - p.Type = types.Number.String() - p.Format = "float" - - case reflect.Bool: - p.Type = types.Boolean.String() - - case reflect.String: - p.Type = types.String.String() - case reflect.Struct: name := makeName(p.GoType) p.Ref = makeRef(name) - - case reflect.Ptr: - p.GoType = t.Elem() - name := makeName(p.GoType) - p.Ref = makeRef(name) - + p.Type = "" case reflect.Map: - p.Type = "object" - p.AddPropertie = buildMapType(p.GoType) - // get the go reflect type of the map value - p.GoType = p.AddPropertie.GoType - - case reflect.Slice: - p.Type = types.Array.String() - p.Items = &Items{} - - // dereference the slice,get the element object of the slice - // Example: - // t ==> *[]*Struct|Struct|*string|string - // p.GoType.Kind() ==> []*Struct|Struct|*string|string - - //p.GoType ==> *Struct|Struct|*string|string + ap := inspect(p.GoType.Elem()) + p.AdditionalProperties = &ap + case reflect.Slice, reflect.Array: + // unwrap array or slice p.GoType = p.GoType.Elem() - - switch p.GoType.Kind() { - case reflect.Ptr: - //p.GoType ==> Struct|string - p.GoType = p.GoType.Elem() - - // determine the type of element object in the slice - isPrimitive := isPrimitiveType(p.GoType.Name(), p.GoType) - - if isPrimitive { - // golang built-in primitive type - kind_type := jsonSchemaType(p.GoType.String(), p.GoType) - p.Items.Type = kind_type - } else { - // Struct types - name := makeName(p.GoType) - p.Items.Ref = makeRef(name) - } - - case reflect.Struct: - name := makeName(p.GoType) - p.Items.Ref = makeRef(name) - - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Uint8, reflect.Uint16, reflect.Uint32: - p.Items.Type = types.Integer.String() - p.Items.Format = "int32" - - case reflect.Int64, reflect.Uint64: - p.Items.Type = types.Integer.String() - p.Items.Format = "int64" - - case reflect.Float64: - p.Items.Type = types.Number.String() - p.Items.Format = "double" - - case reflect.Float32: - p.Items.Type = types.Number.String() - p.Items.Format = "float" - - case reflect.String: - p.Items.Type = types.String.String() - } + items := inspect(p.GoType) + p.Items = &items + default: } - return p } -// buildMapType build map type swag info -func buildMapType(mapType reflect.Type) *AdditionalProperties { - - prop := AdditionalProperties{} - - // get the element object of the map - // Example: - // mapType ==> map[string][]*Struct|Struct|*string|string - // or mapTYpe ==> map[string]*Struct|Struct|*string|string - // mapType.Elem().Kind() ==> []*Struct|Struct|*string|string - // or mapType.Elem().Kind() ==> *Struct|Struct|*string|string - if mapType.Elem().Kind().String() != "interface" { - isSlice := isSliceOrArryType(mapType.Elem().Kind()) - if isSlice || isByteArrayType(mapType.Elem()) { - // if map value is slice - // Example: - // mapType.Elem()==> []*Struct|Struct|*string|string - mapType = mapType.Elem() - } - - // if map value is struct or built-in primitive type - // Example: - // mapType.Elem()==> *Struct|Struct|*string|string - isPrimitive := isPrimitiveType(mapType.Elem().Name(), mapType.Elem()) - - if isByteArrayType(mapType.Elem()) { - prop.Type = "string" - } else { - if isSlice { - prop.Type = types.Array.String() - prop.Items = &Items{} - if isPrimitive { - prop.Items.Type = jsonSchemaType(mapType.Elem().String(), mapType.Elem()) - prop.GoType = getGoType(mapType.Elem()) - } else { - prop.Items.Ref = makeMapRef(mapType.Elem().String()) - prop.GoType = getGoType(mapType.Elem()) - } - } else if isPrimitive { - prop.Type = jsonSchemaType(mapType.Elem().String(), mapType.Elem()) - prop.GoType = getGoType(mapType.Elem()) - } else { - prop.Ref = makeMapRef(mapType.Elem().String()) - prop.GoType = getGoType(mapType.Elem()) - } - } - } - - return &prop -} - -func getGoType(t reflect.Type) reflect.Type { - - var goType reflect.Type - - if t.Kind() == reflect.Ptr { - goType = t.Elem() - } else { - goType = t - } - - return goType -} - -func makeMapRef(typeName string) string { - type_name := strings.Trim(typeName, "*") - return makeRef(type_name) -} - -func isSliceOrArryType(t reflect.Kind) bool { - return t == reflect.Slice || t == reflect.Array -} - -// isByteArrayType -// Check if the data is of slice or array type -// while simultaneously verifying if the element type is of byte type(in go defined "type byte=uint8"). -func isByteArrayType(t reflect.Type) bool { - return (t.Kind() == reflect.Slice || t.Kind() == reflect.Array) && - t.Elem().Kind() == reflect.Uint8 -} - -// isPrimitiveType Whether it is a built-in primitive type -func isPrimitiveType(modelName string, modelType reflect.Type) bool { - var modelKind reflect.Kind - - if modelType.Kind() == reflect.Ptr { - modelKind = modelType.Elem().Kind() - } else { - modelKind = modelType.Kind() - } - - switch modelKind { - case reflect.Bool: - return true - case reflect.Float32, reflect.Float64, - reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, - reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: - return true - case reflect.String: - return true - } - - if len(modelName) == 0 { - return false - } - - _, ok := types.SchemaMap[modelName] - return ok -} - -func jsonSchemaType(modelName string, modelType reflect.Type) string { - var modelKind reflect.Kind - - if modelType.Kind() == reflect.Ptr { - modelKind = modelType.Elem().Kind() - } else { - modelKind = modelType.Kind() - } - - if mapped, ok := types.SchemaMap[modelName]; ok { - return mapped - } - - // check if original type is primitive - switch modelKind { - case reflect.Bool: - return types.Boolean.String() - case reflect.Float32, reflect.Float64: - return types.Number.String() - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, - reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: - return types.Integer.String() - case reflect.String: - return types.String.String() - } - - return modelName // use as is (custom or struct) -} - func buildProperty(t reflect.Type) (map[string]Property, []string) { properties := make(map[string]Property) required := make([]string, 0) @@ -305,7 +85,15 @@ func buildProperty(t reflect.Type) (map[string]Property, []string) { // honor json ignore tag continue } - p := inspect(field.Type, field.Tag.Get("json")) + + var p Property + jsonTag := field.Tag.Get("json") + if strings.Contains(jsonTag, ",string") { + p.Type = types.String.String() + p.GoType = field.Type + } else { + p = inspect(field.Type) + } // determine the extra info of the field if _, ok := field.Tag.Lookup("required"); ok { @@ -328,7 +116,7 @@ func buildProperty(t reflect.Type) (map[string]Property, []string) { return properties, required } -func defineObject(v interface{}, desc string) Object { +func defineObject(v interface{}) Object { var t reflect.Type switch value := v.(type) { case reflect.Type: @@ -337,80 +125,76 @@ func defineObject(v interface{}, desc string) Object { t = reflect.TypeOf(v) } - isArray := t.Kind() == reflect.Slice - if isArray { - t = t.Elem() - } - if t.Kind() == reflect.Ptr { - t = t.Elem() + kind := t.Kind() + if kind == reflect.Ptr { + // unwrap pointer + return defineObject(t.Elem()) } - if t.Kind() != reflect.Struct { - p := inspect(t, "") - return Object{ - IsArray: isArray, - GoType: t, - Type: p.Type, - Format: p.Format, - Name: t.Kind().String(), - } + obj := Object{ + Name: kind.String(), } - properties, required := buildProperty(t) - - return Object{ - IsArray: isArray, - GoType: t, - Type: "object", - Name: makeName(t), - Required: required, - Properties: properties, - Description: desc, + switch kind { + case reflect.Slice, reflect.Array: + obj.Name = makeName(t) + t = t.Elem() + p := inspect(t) + obj.Items = &p + obj.Type = types.Array.String() + case reflect.Map: + obj.Name = makeName(t) + t = t.Elem() + p := inspect(t) + obj.AdditionalProperties = &p + obj.Type = types.Object.String() + case reflect.Struct: + obj.Name = makeName(t) + properties, required := buildProperty(t) + obj.Type = types.Object.String() + obj.Required = required + obj.Properties = properties + default: + p := inspect(t) + obj.Type = p.Type + obj.Format = p.Format } + return obj } func define(v interface{}) map[string]Object { - objMap := map[string]Object{} - - obj := defineObject(v, "") + objMap := make(map[string]Object) + obj := defineObject(v) objMap[obj.Name] = obj dirty := true - for dirty { dirty = false for _, d := range objMap { for _, p := range d.Properties { - if p.GoType.Kind() == reflect.Struct { - name := makeName(p.GoType) - if _, exists := objMap[name]; !exists { - child := defineObject(p.GoType, p.Description) - objMap[child.Name] = child - dirty = true + prop := &p + for prop != nil { + if prop.GoType.Kind() == reflect.Struct { + name := makeName(prop.GoType) + if _, exists := objMap[name]; !exists { + child := defineObject(prop.GoType) + objMap[child.Name] = child + dirty = true + } } + prop = prop.AdditionalProperties } } } } - return objMap } // MakeSchema takes struct or pointer to a struct and returns a Schema instance suitable for use by the swagger doc func MakeSchema(prototype interface{}) *Schema { + obj := defineObject(prototype) schema := &Schema{ Prototype: prototype, + Ref: makeRef(obj.Name), } - - obj := defineObject(prototype, "") - if obj.IsArray { - schema.Type = "array" - schema.Items = &Items{ - Ref: makeRef(obj.Name), - } - - } else { - schema.Ref = makeRef(obj.Name) - } - return schema } diff --git a/reflect_test.go b/reflect_test.go index 2877c43..e2937df 100644 --- a/reflect_test.go +++ b/reflect_test.go @@ -19,6 +19,7 @@ import ( "encoding/json" "fmt" "os" + "reflect" "testing" "github.com/stretchr/testify/assert" @@ -54,9 +55,9 @@ type Pet struct { MapSlice map[string][]string MapSliceStructPtr map[string][]*Person MapSliceStruct map[string][]Person - SliceStructPtr *[]*Person + SliceStructPtr []*Person SliceStruct *[]Person - SliceStringPtr *[]*string + SliceStringPtr []*string SliceString *[]string MapNestOptions *MapObj `json:"map_nest_options,omitempty"` } @@ -78,7 +79,6 @@ func TestDefine(t *testing.T) { v := define(Pet{}) obj, ok := v["github.com_zc2638_swag.Pet"] assert.True(t, ok) - assert.False(t, obj.IsArray) assert.Equal(t, 26, len(obj.Properties)) content := make(map[string]Object) @@ -88,7 +88,6 @@ func TestDefine(t *testing.T) { assert.Nil(t, err) expected := content["github.com_zc2638_swag.Pet"] - assert.Equal(t, expected.IsArray, obj.IsArray, "expected IsArray to match") assert.Equal(t, expected.Type, obj.Type, "expected Type to match") assert.Equal(t, expected.Required, obj.Required, "expected Required to match") assert.Equal(t, len(expected.Properties), len(obj.Properties), "expected same number of properties") @@ -96,12 +95,11 @@ func TestDefine(t *testing.T) { for k, v := range obj.Properties { e := expected.Properties[k] assert.Equal(t, e.Type, v.Type, "expected %v.Type to match", k) - assert.Equal(t, e.Description, v.Description, "expected %v.Required to match", k) - assert.Equal(t, e.Enum, v.Enum, "expected %v.Required to match", k) - assert.Equal(t, e.Format, v.Format, "expected %v.Required to match", k) - assert.Equal(t, e.Ref, v.Ref, "expected %v.Required to match", k) - assert.Equal(t, e.Example, v.Example, "expected %v.Required to match", k) - assert.Equal(t, e.Items, v.Items, "expected %v.Required to match", k) + assert.Equal(t, e.Description, v.Description, "expected %v.Description to match", k) + assert.Equal(t, e.Enum, v.Enum, "expected %v.Enum to match", k) + assert.Equal(t, e.Format, v.Format, "expected %v.Format to match", k) + assert.Equal(t, e.Ref, v.Ref, "expected %v.Ref to match", k) + assert.Equal(t, e.Example, v.Example, "expected %v.Example to match", k) } } @@ -109,21 +107,18 @@ func TestNotStructDefine(t *testing.T) { v := define(int32(1)) obj, ok := v["int32"] assert.True(t, ok) - assert.False(t, obj.IsArray) assert.Equal(t, "integer", obj.Type) assert.Equal(t, "int32", obj.Format) v = define(uint64(1)) obj, ok = v["uint64"] assert.True(t, ok) - assert.False(t, obj.IsArray) assert.Equal(t, "integer", obj.Type) assert.Equal(t, "int64", obj.Format) v = define("") obj, ok = v["string"] assert.True(t, ok) - assert.False(t, obj.IsArray) assert.Equal(t, "string", obj.Type) assert.Equal(t, "", obj.Format) @@ -132,32 +127,23 @@ func TestNotStructDefine(t *testing.T) { if !assert.True(t, ok) { fmt.Printf("%v", v) } - assert.False(t, obj.IsArray) assert.Equal(t, "integer", obj.Type) assert.Equal(t, "int32", obj.Format) - v = define([]byte{1, 2}) - obj, ok = v["uint8"] - if !assert.True(t, ok) { - fmt.Printf("%v", v) - } - assert.True(t, obj.IsArray) - assert.Equal(t, "integer", obj.Type) - assert.Equal(t, "int32", obj.Format) + bs := []byte{1, 2} + v = define(bs) + bsName := makeName(reflect.TypeOf(bs)) + obj, ok = v[bsName] + assert.Equal(t, true, ok) + assert.Equal(t, "array", obj.Type) + assert.NotEqual(t, nil, obj.Items) + assert.Equal(t, "integer", obj.Items.Type) + assert.Equal(t, "int32", obj.Items.Format) } func TestHonorJsonIgnore(t *testing.T) { v := define(Empty{}) obj, ok := v["github.com_zc2638_swag.Empty"] assert.True(t, ok) - assert.False(t, obj.IsArray) assert.Equal(t, 0, len(obj.Properties), "expected zero exposed properties") } - -func TestMakeSchemaType(t *testing.T) { - sliceSchema := MakeSchema([]string{}) - assert.Equal(t, "array", sliceSchema.Type, "expect array type but get %s", sliceSchema.Type) - - objSchema := MakeSchema(struct{}{}) - assert.Equal(t, "", objSchema.Type, "expect array type but get %s", objSchema.Type) -} diff --git a/testdata/pet.json b/testdata/pet.json index ee477f3..fc11d52 100644 --- a/testdata/pet.json +++ b/testdata/pet.json @@ -13,29 +13,14 @@ "github.com_zc2638_swag.MapOption": { "type": "object", "properties": { - "match_src": { - "type": "string" - }, "name": { "type": "string" }, - "ops": { - "type": "array", - "items": { - "type": "string" - } - }, "sub_options": { "type": "object", "additionalProperties": { "$ref": "#/definitions/github.com_zc2638_swag.MapOption" } - }, - "values": { - "type": "array", - "items": { - "type": "string" - } } } }, @@ -205,4 +190,4 @@ } } } -} \ No newline at end of file +} diff --git a/types/convert.go b/types/convert.go new file mode 100644 index 0000000..66257ea --- /dev/null +++ b/types/convert.go @@ -0,0 +1,62 @@ +// Copyright © 2024 zc2638 . +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package types + +import ( + "reflect" +) + +var ptMapping = map[string]ParameterType{ + "time.Time": "string", + "time.Duration": "integer", + "json.Number": "number", +} + +func Register(goType string, pt ParameterType) { + ptMapping[goType] = pt +} + +func Get(goType string) ParameterType { + return ptMapping[goType] +} + +func Parse(t reflect.Type) ParameterType { + kind := t.Kind() + if kind == reflect.Ptr { + return Parse(t.Elem()) + } + + if pt := Get(t.String()); pt != Unknown { + return pt + } + + switch kind { + case reflect.Bool: + return Boolean + case reflect.Float32, reflect.Float64: + return Number + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, + reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: + return Integer + case reflect.String: + return String + case reflect.Array, reflect.Slice: + return Array + case reflect.Struct, reflect.Map: + return Object + default: + return Unknown + } +} diff --git a/types/convert_test.go b/types/convert_test.go new file mode 100644 index 0000000..bb71869 --- /dev/null +++ b/types/convert_test.go @@ -0,0 +1,42 @@ +// Copyright © 2024 zc2638 . +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package types + +import ( + "encoding/json" + "reflect" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestRegister(t *testing.T) { + Register("json.Number", Integer) + assert.Equal(t, Integer, Get("json.Number")) +} + +func TestParse(t *testing.T) { + pointer := &[]int{1, 2, 3} + pt := Parse(reflect.TypeOf(pointer)) + assert.Equal(t, Array, pt) + + jn := json.Number("123") + pt = Parse(reflect.TypeOf(jn)) + assert.Equal(t, Get("json.Number"), pt) + + other := make(chan struct{}) + pt = Parse(reflect.TypeOf(other)) + assert.Equal(t, Unknown, pt) +} diff --git a/types/format.go b/types/format.go new file mode 100644 index 0000000..2dc263c --- /dev/null +++ b/types/format.go @@ -0,0 +1,51 @@ +// Copyright © 2024 zc2638 . +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package types + +import "reflect" + +type Format string + +func (f Format) String() string { + return string(f) +} + +const ( + FormatNone Format = "" + FormatInt32 Format = "int32" + FormatInt64 Format = "int64" + FormatDouble Format = "double" + FormatFloat Format = "float" +) + +func ParseFormat(t reflect.Type) Format { + kind := t.Kind() + if kind == reflect.Ptr { + return ParseFormat(t.Elem()) + } + + switch kind { + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Uint8, reflect.Uint16, reflect.Uint32: + return FormatInt32 + case reflect.Int64, reflect.Uint64: + return FormatInt64 + case reflect.Float64: + return FormatDouble + case reflect.Float32: + return FormatFloat + default: + return FormatNone + } +} diff --git a/types/format_test.go b/types/format_test.go new file mode 100644 index 0000000..4fb983e --- /dev/null +++ b/types/format_test.go @@ -0,0 +1,29 @@ +// Copyright © 2024 zc2638 . +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package types + +import ( + "reflect" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestParseFormat(t *testing.T) { + data := &[]int{1, 2, 3} + rt := reflect.TypeOf(data) + format := ParseFormat(rt) + assert.Equal(t, FormatNone, format) +} diff --git a/types/types.go b/types/types.go index f7eb4e4..30ec2a4 100644 --- a/types/types.go +++ b/types/types.go @@ -26,11 +26,14 @@ func (pt ParameterType) String() string { } const ( + Unknown ParameterType = "" + Integer ParameterType = "integer" Number ParameterType = "number" Boolean ParameterType = "boolean" String ParameterType = "string" Array ParameterType = "array" + Object ParameterType = "object" File ParameterType = "file" ) @@ -45,12 +48,6 @@ type Context struct { PathParams map[string]string } -var SchemaMap = map[string]string{ - "time.Time": "string", - "time.Duration": "integer", - "json.Number": "number", -} - // AddURLParamsToContext returns a copy of parent in which the context value is set func AddURLParamsToContext(parent context.Context, params map[string]string) context.Context { routeVal := parent.Value(RouteContextKey) diff --git a/types/types_test.go b/types/types_test.go index 9613d28..c2eb2b2 100644 --- a/types/types_test.go +++ b/types/types_test.go @@ -1,5 +1,17 @@ -// Package types -// Created by zc on 2022/6/11. +// Copyright © 2022 zc2638 . +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package types import (