github.com/unionj-cloud/go-doudou@v1.3.8-0.20221011095552-0088008e5b31/cmd/internal/openapi/v3/helper.go (about) 1 package v3 2 3 import ( 4 "encoding/json" 5 "github.com/unionj-cloud/go-doudou/cmd/internal/astutils" 6 "github.com/unionj-cloud/go-doudou/toolkit/copier" 7 . "github.com/unionj-cloud/go-doudou/toolkit/openapi/v3" 8 "github.com/unionj-cloud/go-doudou/toolkit/sliceutils" 9 "github.com/unionj-cloud/go-doudou/toolkit/stringutils" 10 "regexp" 11 "strings" 12 "unicode" 13 ) 14 15 // Schemas from components of OpenAPI3.0 json document 16 var Schemas = make(map[string]Schema) 17 18 var Enums = make(map[string]astutils.EnumMeta) 19 20 // SchemaNames schema names from components of OpenAPI3.0 json document 21 // also struct names from vo package 22 var SchemaNames []string 23 24 // SchemaOf reference https://golang.org/pkg/builtin/ 25 // type bool 26 // type byte 27 // type complex128 28 // type complex64 29 // type error 30 // type float32 31 // type float64 32 // type int 33 // type int16 34 // type int32 35 // type int64 36 // type int8 37 // type rune 38 // type string 39 // type uint 40 // type uint16 41 // type uint32 42 // type uint64 43 // type uint8 44 // type uintptr 45 func SchemaOf(field astutils.FieldMeta) *Schema { 46 ft := field.Type 47 if IsVarargs(ft) { 48 ft = ToSlice(ft) 49 } 50 ft = strings.TrimLeft(ft, "*") 51 switch ft { 52 case "int", "int8", "int16", "int32", "uint", "uint8", "uint16", "uint32", "byte", "rune", "complex64", "complex128": 53 return Int 54 case "int64", "uint64", "uintptr": 55 return Int64 56 case "bool": 57 return Bool 58 case "string", "error", "[]rune", "[]byte": 59 return String 60 case "float32": 61 return Float32 62 case "float64": 63 return Float64 64 case "multipart.FileHeader", "v3.FileModel": 65 return File 66 default: 67 return handleDefaultCase(ft) 68 } 69 } 70 71 func handleDefaultCase(ft string) *Schema { 72 if strings.HasPrefix(ft, "map[") { 73 elem := ft[strings.Index(ft, "]")+1:] 74 return &Schema{ 75 Type: ObjectT, 76 AdditionalProperties: SchemaOf(astutils.FieldMeta{ 77 Type: elem, 78 }), 79 } 80 } 81 if strings.HasPrefix(ft, "[") { 82 elem := ft[strings.Index(ft, "]")+1:] 83 return &Schema{ 84 Type: ArrayT, 85 Items: SchemaOf(astutils.FieldMeta{ 86 Type: elem, 87 }), 88 } 89 } 90 re := regexp.MustCompile(`anonystruct«(.*)»`) 91 if re.MatchString(ft) { 92 result := re.FindStringSubmatch(ft) 93 var structmeta astutils.StructMeta 94 json.Unmarshal([]byte(result[1]), &structmeta) 95 schema := NewSchema(structmeta) 96 return &schema 97 } 98 var title string 99 if !strings.Contains(ft, ".") { 100 title = ft 101 } 102 if stringutils.IsEmpty(title) { 103 title = ft[strings.LastIndex(ft, ".")+1:] 104 } 105 if stringutils.IsNotEmpty(title) { 106 if unicode.IsUpper(rune(title[0])) { 107 if sliceutils.StringContains(SchemaNames, title) { 108 return &Schema{ 109 Ref: "#/components/schemas/" + title, 110 } 111 } 112 } 113 if enumMeta, ok := Enums[title]; ok { 114 enumSchema := &Schema{ 115 Type: StringT, 116 Enum: sliceutils.StringSlice2InterfaceSlice(enumMeta.Values), 117 } 118 if len(enumMeta.Values) > 0 { 119 enumSchema.Default = enumMeta.Values[0] 120 } 121 return enumSchema 122 } 123 } 124 return Any 125 } 126 127 var castFuncMap = map[string]string{ 128 "bool": "ToBool", 129 "float64": "ToFloat64", 130 "float32": "ToFloat32", 131 "int64": "ToInt64", 132 "int32": "ToInt32", 133 "int16": "ToInt16", 134 "int8": "ToInt8", 135 "int": "ToInt", 136 "uint": "ToUint", 137 "uint8": "ToUint8", 138 "uint16": "ToUint16", 139 "uint32": "ToUint32", 140 "uint64": "ToUint64", 141 "error": "ToError", 142 "[]byte": "ToByteSlice", 143 "[]rune": "ToRuneSlice", 144 "[]interface{}": "ToInterfaceSlice", 145 "[]bool": "ToBoolSlice", 146 "[]int": "ToIntSlice", 147 "[]float64": "ToFloat64Slice", 148 "[]float32": "ToFloat32Slice", 149 "[]int64": "ToInt64Slice", 150 "[]int32": "ToInt32Slice", 151 "[]int16": "ToInt16Slice", 152 "[]int8": "ToInt8Slice", 153 "[]uint": "ToUintSlice", 154 "[]uint8": "ToUint8Slice", 155 "[]uint16": "ToUint16Slice", 156 "[]uint32": "ToUint32Slice", 157 "[]uint64": "ToUint64Slice", 158 "[]error": "ToErrorSlice", 159 "[][]byte": "ToByteSliceSlice", 160 "[][]rune": "ToRuneSliceSlice", 161 } 162 163 func IsSupport(t string) bool { 164 if IsVarargs(t) { 165 t = ToSlice(t) 166 } 167 _, exists := castFuncMap[strings.TrimLeft(t, "*")] 168 return exists 169 } 170 171 func IsOptional(t string) bool { 172 return strings.HasPrefix(t, "*") || strings.HasPrefix(t, "...") 173 } 174 175 func IsSlice(t string) bool { 176 return strings.Contains(t, "[") || strings.HasPrefix(t, "...") 177 } 178 179 func IsVarargs(t string) bool { 180 return strings.HasPrefix(t, "...") 181 } 182 183 func ToSlice(t string) string { 184 return "[]" + strings.TrimPrefix(t, "...") 185 } 186 187 func CastFunc(t string) string { 188 if IsVarargs(t) { 189 t = ToSlice(t) 190 } 191 return castFuncMap[strings.TrimLeft(t, "*")] 192 } 193 194 // CopySchema as SchemaOf returns pointer, so deepcopy the schema the pointer points 195 func CopySchema(field astutils.FieldMeta) Schema { 196 var schema Schema 197 err := copier.DeepCopy(SchemaOf(field), &schema) 198 if err != nil { 199 panic(err) 200 } 201 return schema 202 } 203 204 func RefAddDoc(schema *Schema, doc string) { 205 if stringutils.IsNotEmpty(schema.Ref) { 206 title := strings.TrimPrefix(schema.Ref, "#/components/schemas/") 207 temp := Schemas[title] 208 temp.Description = strings.Join([]string{doc, temp.Description}, "\n") 209 Schemas[title] = temp 210 } else { 211 schema.Description = doc 212 } 213 } 214 215 // NewSchema new schema from astutils.StructMeta 216 func NewSchema(structmeta astutils.StructMeta) Schema { 217 properties := make(map[string]*Schema) 218 var required []string 219 for _, field := range structmeta.Fields { 220 fschema := CopySchema(field) 221 RefAddDoc(&fschema, strings.Join(field.Comments, "\n")) 222 properties[field.DocName] = &fschema 223 if !strings.HasPrefix(field.Type, "*") { 224 required = append(required, field.DocName) 225 } 226 } 227 return Schema{ 228 Title: structmeta.Name, 229 Type: ObjectT, 230 Properties: properties, 231 Description: strings.Join(structmeta.Comments, "\n"), 232 Required: required, 233 } 234 } 235 236 // IsBuiltin check whether field is built-in type https://pkg.go.dev/builtin or not 237 func IsBuiltin(field astutils.FieldMeta) bool { 238 simples := []interface{}{Int, Int64, Bool, String, Float32, Float64} 239 types := []interface{}{IntegerT, StringT, BooleanT, NumberT} 240 pschema := SchemaOf(field) 241 if sliceutils.Contains(simples, pschema) || (sliceutils.Contains(types, pschema.Type) && pschema.Format != BinaryF) { 242 return true 243 } 244 if pschema.Type == ArrayT && (sliceutils.Contains(simples, pschema.Items) || (sliceutils.Contains(types, pschema.Items.Type) && pschema.Items.Format != BinaryF)) { 245 return true 246 } 247 return false 248 } 249 250 // IsEnum check whether field is enum 251 func IsEnum(field astutils.FieldMeta) bool { 252 pschema := SchemaOf(field) 253 return len(pschema.Enum) > 0 || (pschema.Type == ArrayT && len(pschema.Items.Enum) > 0) 254 } 255 256 // IsStruct check whether field is struct type 257 func IsStruct(field astutils.FieldMeta) bool { 258 return stringutils.IsNotEmpty(SchemaOf(field).Ref) 259 } 260 261 // ElementType get element type string from slice 262 func ElementType(t string) string { 263 if IsVarargs(t) { 264 return strings.TrimPrefix(t, "...") 265 } 266 return t[strings.Index(t, "]")+1:] 267 }