79 lines
2.7 KiB
Go
79 lines
2.7 KiB
Go
package apigen
|
|
|
|
import (
|
|
"fmt"
|
|
"git.yuntuo.hk/k2safe/go-omni/contract"
|
|
"git.yuntuo.hk/k2safe/go-omni/openapi"
|
|
"reflect"
|
|
"strings"
|
|
)
|
|
|
|
// Response roots allow named DTOs, []DTO, []primitive, *DTO and null.
|
|
// A nil root slice is always encoded as an empty JSON array.
|
|
func responseShape(value string) (kind, name string) {
|
|
if value == "null" {
|
|
return "null", ""
|
|
}
|
|
if strings.HasPrefix(value, "[]") {
|
|
return "array", strings.TrimPrefix(value, "[]")
|
|
}
|
|
if strings.HasPrefix(value, "*") {
|
|
return "nullable", strings.TrimPrefix(value, "*")
|
|
}
|
|
return "object", value
|
|
}
|
|
func responseGoType(value string) string {
|
|
kind, name := responseShape(value)
|
|
if kind == "null" || value == "" {
|
|
return ""
|
|
}
|
|
if kind == "array" {
|
|
if primitiveTypes[name] != nil {
|
|
return "[]" + name
|
|
}
|
|
return "[]types." + name
|
|
}
|
|
return "*types." + name
|
|
}
|
|
func responseDataSchema(value string, bindings map[string]*contract.Binder) (*openapi.Schema, error) {
|
|
kind, name := responseShape(value)
|
|
if kind == "null" {
|
|
return &openapi.Schema{Type: "object", Nullable: true, Enum: []any{nil}}, nil
|
|
}
|
|
if kind == "array" && primitiveTypes[name] != nil {
|
|
if name == "File" {
|
|
return nil, fmt.Errorf("File is request-only; use a response DTO")
|
|
}
|
|
// Reuse the contract compiler, including integer bounds and byte-slice rules.
|
|
t := reflect.StructOf([]reflect.StructField{{Name: "Value", Type: reflect.SliceOf(primitiveTypes[name]), Tag: `json:"value"`}})
|
|
b, err := contract.Describe(t)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
s, err := b.ResponseSchema()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return s.Properties["value"], nil
|
|
}
|
|
b, ok := bindings[name]
|
|
if !ok {
|
|
return nil, fmt.Errorf("unknown response DTO %q; use Name, []Name, []string/[]int64/[]bool/[]any, *Name or null", name)
|
|
}
|
|
s, err := b.ResponseSchema()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
switch kind {
|
|
case "array":
|
|
return &openapi.Schema{Type: "array", Items: s}, nil
|
|
case "nullable":
|
|
s.Nullable = true
|
|
}
|
|
return s, nil
|
|
}
|
|
|
|
var primitiveTypes = map[string]reflect.Type{"File": reflect.TypeOf(contract.File{}), "any": reflect.TypeOf((*any)(nil)).Elem(), "string": reflect.TypeOf(""), "bool": reflect.TypeOf(false), "int": reflect.TypeOf(int(0)), "int8": reflect.TypeOf(int8(0)), "int16": reflect.TypeOf(int16(0)), "int32": reflect.TypeOf(int32(0)), "int64": reflect.TypeOf(int64(0)), "uint": reflect.TypeOf(uint(0)), "uint8": reflect.TypeOf(uint8(0)), "uint16": reflect.TypeOf(uint16(0)), "uint32": reflect.TypeOf(uint32(0)), "uint64": reflect.TypeOf(uint64(0)), "float32": reflect.TypeOf(float32(0)), "float64": reflect.TypeOf(float64(0))}
|
|
|
|
func responseNeedsTypes(value string) bool { return strings.Contains(responseGoType(value), "types.") }
|