summaryrefslogtreecommitdiff
path: root/vendor/github.com/go-swagger/go-swagger/codescan/schema.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/go-swagger/go-swagger/codescan/schema.go')
-rw-r--r--vendor/github.com/go-swagger/go-swagger/codescan/schema.go1092
1 files changed, 802 insertions, 290 deletions
diff --git a/vendor/github.com/go-swagger/go-swagger/codescan/schema.go b/vendor/github.com/go-swagger/go-swagger/codescan/schema.go
index 640ac0830..670b2ece1 100644
--- a/vendor/github.com/go-swagger/go-swagger/codescan/schema.go
+++ b/vendor/github.com/go-swagger/go-swagger/codescan/schema.go
@@ -31,6 +31,8 @@ type schemaTypable struct {
level int
}
+func (st schemaTypable) In() string { return "body" }
+
func (st schemaTypable) Typed(tpe, format string) {
st.schema.Typed(tpe, format)
}
@@ -170,7 +172,7 @@ func (s *schemaBuilder) buildFromDecl(_ *entityDecl, schema *spec.Schema) error
sp.setTitle = func(lines []string) { schema.Title = joinDropLast(lines) }
sp.setDescription = func(lines []string) {
schema.Description = joinDropLast(lines)
- enumDesc := getEnumDesc(schema.VendorExtensible.Extensions)
+ enumDesc := getEnumDesc(schema.Extensions)
if enumDesc != "" {
schema.Description += "\n" + enumDesc
}
@@ -184,60 +186,83 @@ func (s *schemaBuilder) buildFromDecl(_ *entityDecl, schema *spec.Schema) error
return nil
}
- switch tpe := s.decl.Type.Obj().Type().(type) {
+ defer func() {
+ if schema.Ref.String() == "" {
+ // unless this is a $ref, we add traceability of the origin of this schema in source
+ if s.Name != s.GoName {
+ addExtension(&schema.VendorExtensible, "x-go-name", s.GoName)
+ }
+ addExtension(&schema.VendorExtensible, "x-go-package", s.decl.Obj().Pkg().Path())
+ }
+ }()
+
+ switch tpe := s.decl.ObjType().(type) {
+ // TODO(fredbi): we may safely remove all the cases here that are not Named or Alias
case *types.Basic:
debugLog("basic: %v", tpe.Name())
+ return nil
case *types.Struct:
- if err := s.buildFromStruct(s.decl, tpe, schema, make(map[string]string)); err != nil {
- return err
- }
+ return s.buildFromStruct(s.decl, tpe, schema, make(map[string]string))
case *types.Interface:
- if err := s.buildFromInterface(s.decl, tpe, schema, make(map[string]string)); err != nil {
- return err
- }
+ return s.buildFromInterface(s.decl, tpe, schema, make(map[string]string))
case *types.Array:
debugLog("array: %v -> %v", s.decl.Ident.Name, tpe.Elem().String())
+ return nil
case *types.Slice:
debugLog("slice: %v -> %v", s.decl.Ident.Name, tpe.Elem().String())
+ return nil
case *types.Map:
debugLog("map: %v -> [%v]%v", s.decl.Ident.Name, tpe.Key().String(), tpe.Elem().String())
+ return nil
case *types.Named:
- o := tpe.Obj()
- if o != nil {
- debugLog("got the named type object: %s.%s | isAlias: %t | exported: %t", o.Pkg().Path(), o.Name(), o.IsAlias(), o.Exported())
- if o.Pkg().Name() == "time" && o.Name() == "Time" {
- schema.Typed("string", "date-time")
- return nil
- }
+ debugLog("named: %v", tpe)
+ return s.buildDeclNamed(tpe, schema)
+ case *types.Alias:
+ debugLog("alias: %v -> %v", tpe, tpe.Rhs())
+ tgt := schemaTypable{schema, 0}
- ps := schemaTypable{schema, 0}
- for {
- ti := s.decl.Pkg.TypesInfo.Types[s.decl.Spec.Type]
- if ti.IsBuiltin() {
- break
- }
- if ti.IsType() {
- if err := s.buildFromType(ti.Type, ps); err != nil {
- return err
- }
- break
- }
- }
- }
+ return s.buildDeclAlias(tpe, tgt)
+ case *types.TypeParam:
+ log.Printf("WARNING: generic type parameters are not supported yet %[1]v (%[1]T). Skipped", tpe)
+ return nil
+ case *types.Chan:
+ log.Printf("WARNING: channels are not supported %[1]v (%[1]T). Skipped", tpe)
+ return nil
+ case *types.Signature:
+ log.Printf("WARNING: functions are not supported %[1]v (%[1]T). Skipped", tpe)
+ return nil
default:
- log.Printf("WARNING: Missing parser for a %T, skipping model: %s\n", tpe, s.Name)
+ log.Printf("WARNING: missing parser for type %T, skipping model: %s\n", tpe, s.Name)
return nil
}
+}
- if schema.Ref.String() == "" {
- if s.Name != s.GoName {
- addExtension(&schema.VendorExtensible, "x-go-name", s.GoName)
- }
- addExtension(&schema.VendorExtensible, "x-go-package", s.decl.Type.Obj().Pkg().Path())
+func (s *schemaBuilder) buildDeclNamed(tpe *types.Named, schema *spec.Schema) error {
+ if unsupportedBuiltin(tpe) {
+ log.Printf("WARNING: skipped unsupported builtin type: %v", tpe)
+
+ return nil
}
- return nil
+ o := tpe.Obj()
+
+ mustNotBeABuiltinType(o)
+
+ debugLog("got the named type object: %s.%s | isAlias: %t | exported: %t", o.Pkg().Path(), o.Name(), o.IsAlias(), o.Exported())
+ if isStdTime(o) {
+ schema.Typed("string", "date-time")
+ return nil
+ }
+
+ ps := schemaTypable{schema, 0}
+ ti := s.decl.Pkg.TypesInfo.Types[s.decl.Spec.Type]
+ if !ti.IsType() {
+ return fmt.Errorf("declaration is not a type: %v", o)
+ }
+
+ return s.buildFromType(ti.Type, ps)
}
+// buildFromTextMarshal renders a type that marshals as text as a string
func (s *schemaBuilder) buildFromTextMarshal(tpe types.Type, tgt swaggerTypable) error {
if typePtr, ok := tpe.(*types.Pointer); ok {
return s.buildFromTextMarshal(typePtr.Elem(), tgt)
@@ -250,7 +275,8 @@ func (s *schemaBuilder) buildFromTextMarshal(tpe types.Type, tgt swaggerTypable)
}
tio := typeNamed.Obj()
- if tio.Pkg() == nil && tio.Name() == "error" {
+ if isStdError(tio) {
+ tgt.AddExtension("x-go-type", tio.Name())
return swaggerSchemaForType(tio.Name(), tgt)
}
@@ -264,17 +290,20 @@ func (s *schemaBuilder) buildFromTextMarshal(tpe types.Type, tgt swaggerTypable)
if !found {
// this must be a builtin
- debugLog("skipping because package is nil: %s", tpe.String())
+ debugLog("skipping because package is nil: %v", tpe)
return nil
}
- if pkg.Name == "time" && tio.Name() == "Time" {
+
+ if isStdTime(tio) {
tgt.Typed("string", "date-time")
return nil
}
- if pkg.PkgPath == "encoding/json" && tio.Name() == "RawMessage" {
- tgt.Typed("object", "")
+
+ if isStdJSONRawMessage(tio) {
+ tgt.Typed("object", "") // TODO: this should actually be any type
return nil
}
+
cmt, hasComments := s.ctx.FindComments(pkg, tio.Name())
if !hasComments {
cmt = new(ast.CommentGroup)
@@ -286,26 +315,26 @@ func (s *schemaBuilder) buildFromTextMarshal(tpe types.Type, tgt swaggerTypable)
}
tgt.Typed("string", "")
+ tgt.AddExtension("x-go-type", tio.Pkg().Path()+"."+tio.Name())
+
return nil
}
func (s *schemaBuilder) buildFromType(tpe types.Type, tgt swaggerTypable) error {
- pkg, err := importer.Default().Import("encoding")
- if err != nil {
- return nil
- }
- ifc := pkg.Scope().Lookup("TextMarshaler").Type().Underlying().(*types.Interface)
-
// check if the type implements encoding.TextMarshaler interface
- isTextMarshaler := types.Implements(tpe, ifc)
- if isTextMarshaler {
+ // if so, the type is rendered as a string.
+ debugLog("schema buildFromType %v (%T)", tpe, tpe)
+
+ if isTextMarshaler(tpe) {
return s.buildFromTextMarshal(tpe, tgt)
}
switch titpe := tpe.(type) {
- case *types.Alias:
- return nil // resolves panic
case *types.Basic:
+ if unsupportedBuiltinType(titpe) {
+ log.Printf("WARNING: skipped unsupported builtin type: %v", tpe)
+ return nil
+ }
return swaggerSchemaForType(titpe.String(), tgt)
case *types.Pointer:
return s.buildFromType(titpe.Elem(), tgt)
@@ -314,167 +343,451 @@ func (s *schemaBuilder) buildFromType(tpe types.Type, tgt swaggerTypable) error
case *types.Interface:
return s.buildFromInterface(s.decl, titpe, tgt.Schema(), make(map[string]string))
case *types.Slice:
+ // anonymous slice
return s.buildFromType(titpe.Elem(), tgt.Items())
case *types.Array:
+ // anonymous array
return s.buildFromType(titpe.Elem(), tgt.Items())
case *types.Map:
- // debugLog("map: %v -> [%v]%v", fld.Name(), ftpe.Key().String(), ftpe.Elem().String())
- // check if key is a string type, if not print a message
- // and skip the map property. Only maps with string keys can go into additional properties
- sch := tgt.Schema()
- if sch == nil {
- return errors.New("items doesn't support maps")
- }
- eleProp := schemaTypable{sch, tgt.Level()}
- key := titpe.Key()
- isTextMarshaler := types.Implements(key, ifc)
- if key.Underlying().String() == "string" || isTextMarshaler {
- return s.buildFromType(titpe.Elem(), eleProp.AdditionalProperties())
- }
+ return s.buildFromMap(titpe, tgt)
case *types.Named:
- tio := titpe.Obj()
- if tio.Pkg() == nil && tio.Name() == "error" {
- return swaggerSchemaForType(tio.Name(), tgt)
- }
- debugLog("named refined type %s.%s", tio.Pkg().Path(), tio.Name())
- pkg, found := s.ctx.PkgForType(tpe)
- if !found {
- // this must be a builtin
- debugLog("skipping because package is nil: %s", tpe.String())
+ // a named type, e.g. type X struct {}
+ return s.buildNamedType(titpe, tgt)
+ case *types.Alias:
+ // a named alias, e.g. type X = {RHS type}.
+ debugLog("alias(schema.buildFromType): got alias %v to %v", titpe, titpe.Rhs())
+ return s.buildAlias(titpe, tgt)
+ case *types.TypeParam:
+ log.Printf("WARNING: generic type parameters are not supported yet %[1]v (%[1]T). Skipped", titpe)
+ return nil
+ case *types.Chan:
+ log.Printf("WARNING: channels are not supported %[1]v (%[1]T). Skipped", tpe)
+ return nil
+ case *types.Signature:
+ log.Printf("WARNING: functions are not supported %[1]v (%[1]T). Skipped", tpe)
+ return nil
+ default:
+ panic(fmt.Errorf("ERROR: can't determine refined type %[1]v (%[1]T): %w", titpe, ErrInternal))
+ }
+}
+
+func (s *schemaBuilder) buildNamedType(titpe *types.Named, tgt swaggerTypable) error {
+ tio := titpe.Obj()
+ if unsupportedBuiltin(titpe) {
+ log.Printf("WARNING: skipped unsupported builtin type: %v", titpe)
+ return nil
+ }
+ if isAny(tio) {
+ // e.g type X any or type X interface{}
+ _ = tgt.Schema()
+
+ return nil
+ }
+
+ // special case of the "error" interface.
+ if isStdError(tio) {
+ tgt.AddExtension("x-go-type", tio.Name())
+ return swaggerSchemaForType(tio.Name(), tgt)
+ }
+
+ // special case of the "time.Time" type
+ if isStdTime(tio) {
+ tgt.Typed("string", "date-time")
+ return nil
+ }
+
+ // special case of the "json.RawMessage" type
+ if isStdJSONRawMessage(tio) {
+ tgt.Typed("object", "") // TODO: this should actually be any type
+ return nil
+ }
+
+ pkg, found := s.ctx.PkgForType(titpe)
+ debugLog("named refined type %s.%s", pkg, tio.Name())
+ if !found {
+ // this must be a builtin
+ //
+ // This could happen for example when using unsupported types such as complex64, complex128, uintptr,
+ // or type constraints such as comparable.
+ debugLog("skipping because package is nil (builtin type): %v", tio)
+
+ return nil
+ }
+
+ cmt, hasComments := s.ctx.FindComments(pkg, tio.Name())
+ if !hasComments {
+ cmt = new(ast.CommentGroup)
+ }
+
+ if typeName, ok := typeName(cmt); ok {
+ _ = swaggerSchemaForType(typeName, tgt)
+
+ return nil
+ }
+
+ if s.decl.Spec.Assign.IsValid() {
+ debugLog("found assignment: %s.%s", tio.Pkg().Path(), tio.Name())
+ return s.buildFromType(titpe.Underlying(), tgt)
+ }
+
+ if titpe.TypeArgs() != nil && titpe.TypeArgs().Len() > 0 {
+ return s.buildFromType(titpe.Underlying(), tgt)
+ }
+
+ // invariant: the Underlying cannot be an alias or named type
+ switch utitpe := titpe.Underlying().(type) {
+ case *types.Struct:
+ debugLog("found struct: %s.%s", tio.Pkg().Path(), tio.Name())
+
+ decl, ok := s.ctx.FindModel(tio.Pkg().Path(), tio.Name())
+ if !ok {
+ debugLog("could not find model in index: %s.%s", tio.Pkg().Path(), tio.Name())
return nil
}
- if pkg.Name == "time" && tio.Name() == "Time" {
+
+ o := decl.Obj()
+ if isStdTime(o) {
tgt.Typed("string", "date-time")
return nil
}
- if pkg.PkgPath == "encoding/json" && tio.Name() == "RawMessage" {
- tgt.Typed("object", "")
+
+ if sfnm, isf := strfmtName(cmt); isf {
+ tgt.Typed("string", sfnm)
return nil
}
- cmt, hasComments := s.ctx.FindComments(pkg, tio.Name())
- if !hasComments {
- cmt = new(ast.CommentGroup)
- }
if typeName, ok := typeName(cmt); ok {
_ = swaggerSchemaForType(typeName, tgt)
return nil
}
- if s.decl.Spec.Assign.IsValid() {
- return s.buildFromType(titpe.Underlying(), tgt)
+ return s.makeRef(decl, tgt)
+ case *types.Interface:
+ debugLog("found interface: %s.%s", tio.Pkg().Path(), tio.Name())
+
+ decl, found := s.ctx.FindModel(tio.Pkg().Path(), tio.Name())
+ if !found {
+ return fmt.Errorf("can't find source file for type: %v", utitpe)
}
- if titpe.TypeArgs() != nil && titpe.TypeArgs().Len() > 0 {
- return s.buildFromType(titpe.Underlying(), tgt)
+ return s.makeRef(decl, tgt)
+ case *types.Basic:
+ if unsupportedBuiltinType(utitpe) {
+ log.Printf("WARNING: skipped unsupported builtin type: %v", utitpe)
+ return nil
}
- switch utitpe := tpe.Underlying().(type) {
- case *types.Struct:
- if decl, ok := s.ctx.FindModel(tio.Pkg().Path(), tio.Name()); ok {
- if decl.Type.Obj().Pkg().Path() == "time" && decl.Type.Obj().Name() == "Time" {
- tgt.Typed("string", "date-time")
- return nil
- }
- if sfnm, isf := strfmtName(cmt); isf {
- tgt.Typed("string", sfnm)
- return nil
- }
- if typeName, ok := typeName(cmt); ok {
- _ = swaggerSchemaForType(typeName, tgt)
- return nil
- }
+ debugLog("found primitive type: %s.%s", tio.Pkg().Path(), tio.Name())
- return s.makeRef(decl, tgt)
- }
- case *types.Interface:
- if decl, ok := s.ctx.FindModel(tio.Pkg().Path(), tio.Name()); ok {
- return s.makeRef(decl, tgt)
+ if sfnm, isf := strfmtName(cmt); isf {
+ tgt.Typed("string", sfnm)
+ return nil
+ }
+
+ if enumName, ok := enumName(cmt); ok {
+ enumValues, enumDesces, _ := s.ctx.FindEnumValues(pkg, enumName)
+ if len(enumValues) > 0 {
+ tgt.WithEnum(enumValues...)
+ enumTypeName := reflect.TypeOf(enumValues[0]).String()
+ _ = swaggerSchemaForType(enumTypeName, tgt)
}
- case *types.Basic:
- if sfnm, isf := strfmtName(cmt); isf {
- tgt.Typed("string", sfnm)
- return nil
+ if len(enumDesces) > 0 {
+ tgt.WithEnumDescription(strings.Join(enumDesces, "\n"))
}
+ return nil
+ }
- if enumName, ok := enumName(cmt); ok {
- enumValues, enumDesces, _ := s.ctx.FindEnumValues(pkg, enumName)
- if len(enumValues) > 0 {
- tgt.WithEnum(enumValues...)
- enumTypeName := reflect.TypeOf(enumValues[0]).String()
- _ = swaggerSchemaForType(enumTypeName, tgt)
- }
- if len(enumDesces) > 0 {
- tgt.WithEnumDescription(strings.Join(enumDesces, "\n"))
- }
- return nil
- }
+ if defaultName, ok := defaultName(cmt); ok {
+ debugLog("default name: %s", defaultName)
+ return nil
+ }
- if defaultName, ok := defaultName(cmt); ok {
- debugLog(defaultName) //nolint:govet
+ if typeName, ok := typeName(cmt); ok {
+ _ = swaggerSchemaForType(typeName, tgt)
+ return nil
+
+ }
+
+ if isAliasParam(tgt) || aliasParam(cmt) {
+ err := swaggerSchemaForType(utitpe.Name(), tgt)
+ if err == nil {
return nil
}
+ }
- if typeName, ok := typeName(cmt); ok {
- _ = swaggerSchemaForType(typeName, tgt)
- return nil
+ if decl, ok := s.ctx.FindModel(tio.Pkg().Path(), tio.Name()); ok {
+ return s.makeRef(decl, tgt)
+ }
- }
+ return swaggerSchemaForType(utitpe.String(), tgt)
+ case *types.Array:
+ debugLog("found array type: %s.%s", tio.Pkg().Path(), tio.Name())
- if isAliasParam(tgt) || aliasParam(cmt) {
- err := swaggerSchemaForType(utitpe.Name(), tgt)
- if err == nil {
- return nil
- }
+ if sfnm, isf := strfmtName(cmt); isf {
+ if sfnm == "byte" {
+ tgt.Typed("string", sfnm)
+ return nil
}
- if decl, ok := s.ctx.FindModel(tio.Pkg().Path(), tio.Name()); ok {
- return s.makeRef(decl, tgt)
+ if sfnm == "bsonobjectid" {
+ tgt.Typed("string", sfnm)
+ return nil
}
- return swaggerSchemaForType(utitpe.String(), tgt)
- case *types.Array:
- if sfnm, isf := strfmtName(cmt); isf {
- if sfnm == "byte" {
- tgt.Typed("string", sfnm)
- return nil
- }
- if sfnm == "bsonobjectid" {
- tgt.Typed("string", sfnm)
- return nil
- }
- tgt.Items().Typed("string", sfnm)
+ tgt.Items().Typed("string", sfnm)
+ return nil
+ }
+ if decl, ok := s.ctx.FindModel(tio.Pkg().Path(), tio.Name()); ok {
+ return s.makeRef(decl, tgt)
+ }
+ return s.buildFromType(utitpe.Elem(), tgt.Items())
+ case *types.Slice:
+ debugLog("found slice type: %s.%s", tio.Pkg().Path(), tio.Name())
+
+ if sfnm, isf := strfmtName(cmt); isf {
+ if sfnm == "byte" {
+ tgt.Typed("string", sfnm)
return nil
}
- if decl, ok := s.ctx.FindModel(tio.Pkg().Path(), tio.Name()); ok {
- return s.makeRef(decl, tgt)
+ tgt.Items().Typed("string", sfnm)
+ return nil
+ }
+ if decl, ok := s.ctx.FindModel(tio.Pkg().Path(), tio.Name()); ok {
+ return s.makeRef(decl, tgt)
+ }
+ return s.buildFromType(utitpe.Elem(), tgt.Items())
+ case *types.Map:
+ debugLog("found map type: %s.%s", tio.Pkg().Path(), tio.Name())
+
+ if decl, ok := s.ctx.FindModel(tio.Pkg().Path(), tio.Name()); ok {
+ return s.makeRef(decl, tgt)
+ }
+ return nil
+ case *types.TypeParam:
+ log.Printf("WARNING: generic type parameters are not supported yet %[1]v (%[1]T). Skipped", utitpe)
+ return nil
+ case *types.Chan:
+ log.Printf("WARNING: channels are not supported %[1]v (%[1]T). Skipped", utitpe)
+ return nil
+ case *types.Signature:
+ log.Printf("WARNING: functions are not supported %[1]v (%[1]T). Skipped", utitpe)
+ return nil
+ default:
+ log.Printf(
+ "WARNING: can't figure out object type for named type (%T): %v [alias: %t]",
+ titpe.Underlying(), titpe.Underlying(), titpe.Obj().IsAlias(),
+ )
+
+ return nil
+ }
+}
+
+// buildDeclAlias builds a top-level alias declaration.
+func (s *schemaBuilder) buildDeclAlias(tpe *types.Alias, tgt swaggerTypable) error {
+ if unsupportedBuiltinType(tpe) {
+ log.Printf("WARNING: skipped unsupported builtin type: %v", tpe)
+ return nil
+ }
+
+ o := tpe.Obj()
+ if isAny(o) {
+ _ = tgt.Schema() // this is mutating tgt to create an empty schema
+ return nil
+ }
+ if isStdError(o) {
+ tgt.AddExtension("x-go-type", o.Name())
+ return swaggerSchemaForType(o.Name(), tgt)
+ }
+ mustNotBeABuiltinType(o)
+
+ if isStdTime(o) {
+ tgt.Typed("string", "date-time")
+ return nil
+ }
+
+ mustHaveRightHandSide(tpe)
+ rhs := tpe.Rhs()
+
+ decl, ok := s.ctx.FindModel(o.Pkg().Path(), o.Name())
+ if !ok {
+ return fmt.Errorf("can't find source file for aliased type: %v -> %v", tpe, rhs)
+ }
+
+ s.postDecls = append(s.postDecls, decl) // mark the left-hand side as discovered
+
+ if !s.ctx.app.refAliases {
+ // expand alias
+ return s.buildFromType(tpe.Underlying(), tgt)
+ }
+
+ // resolve alias to named type as $ref
+ switch rtpe := rhs.(type) {
+ // named declarations: we construct a $ref to the right-hand side target of the alias
+ case *types.Named:
+ ro := rtpe.Obj()
+ rdecl, found := s.ctx.FindModel(ro.Pkg().Path(), ro.Name())
+ if !found {
+ return fmt.Errorf("can't find source file for target type of alias: %v -> %v", tpe, rtpe)
+ }
+
+ return s.makeRef(rdecl, tgt)
+ case *types.Alias:
+ ro := rtpe.Obj()
+ if unsupportedBuiltin(rtpe) {
+ log.Printf("WARNING: skipped unsupported builtin type: %v", rtpe)
+ return nil
+ }
+ if isAny(ro) {
+ // e.g. type X = any
+ _ = tgt.Schema() // this is mutating tgt to create an empty schema
+ return nil
+ }
+ if isStdError(ro) {
+ // e.g. type X = error
+ tgt.AddExtension("x-go-type", o.Name())
+ return swaggerSchemaForType(o.Name(), tgt)
+ }
+ mustNotBeABuiltinType(ro) // TODO(fred): there are a few other cases
+
+ rdecl, found := s.ctx.FindModel(ro.Pkg().Path(), ro.Name())
+ if !found {
+ return fmt.Errorf("can't find source file for target type of alias: %v -> %v", tpe, rtpe)
+ }
+
+ return s.makeRef(rdecl, tgt)
+ }
+
+ // alias to anonymous type
+ return s.buildFromType(rhs, tgt)
+}
+
+func (s *schemaBuilder) buildAnonymousInterface(it *types.Interface, tgt swaggerTypable, decl *entityDecl) error {
+ tgt.Typed("object", "")
+
+ for i := range it.NumExplicitMethods() {
+ fld := it.ExplicitMethod(i)
+ if !fld.Exported() {
+ continue
+ }
+ sig, isSignature := fld.Type().(*types.Signature)
+ if !isSignature {
+ continue
+ }
+ if sig.Params().Len() > 0 {
+ continue
+ }
+ if sig.Results() == nil || sig.Results().Len() != 1 {
+ continue
+ }
+
+ var afld *ast.Field
+ ans, _ := astutil.PathEnclosingInterval(decl.File, fld.Pos(), fld.Pos())
+ // debugLog("got %d nodes (exact: %t)", len(ans), isExact)
+ for _, an := range ans {
+ at, valid := an.(*ast.Field)
+ if !valid {
+ continue
}
- return s.buildFromType(utitpe.Elem(), tgt.Items())
- case *types.Slice:
- if sfnm, isf := strfmtName(cmt); isf {
- if sfnm == "byte" {
- tgt.Typed("string", sfnm)
- return nil
+
+ debugLog("maybe interface field %s: %s(%T)", fld.Name(), fld.Type().String(), fld.Type())
+ afld = at
+ break
+ }
+
+ if afld == nil {
+ debugLog("can't find source associated with %s for %s", fld.String(), it.String())
+ continue
+ }
+
+ // if the field is annotated with swagger:ignore, ignore it
+ if ignored(afld.Doc) {
+ continue
+ }
+
+ name := fld.Name()
+ if afld.Doc != nil {
+ for _, cmt := range afld.Doc.List {
+ for _, ln := range strings.Split(cmt.Text, "\n") {
+ matches := rxName.FindStringSubmatch(ln)
+ ml := len(matches)
+ if ml > 1 {
+ name = matches[ml-1]
+ }
}
- tgt.Items().Typed("string", sfnm)
- return nil
- }
- if decl, ok := s.ctx.FindModel(tio.Pkg().Path(), tio.Name()); ok {
- return s.makeRef(decl, tgt)
- }
- return s.buildFromType(utitpe.Elem(), tgt.Items())
- case *types.Map:
- if decl, ok := s.ctx.FindModel(tio.Pkg().Path(), tio.Name()); ok {
- return s.makeRef(decl, tgt)
}
- return nil
+ }
- default:
- log.Printf("WARNING: can't figure out object type for named type (%T): %v [alias: %t]", tpe.Underlying(), tpe.Underlying(), titpe.Obj().IsAlias())
+ if tgt.Schema().Properties == nil {
+ tgt.Schema().Properties = make(map[string]spec.Schema)
+ }
+ ps := tgt.Schema().Properties[name]
+ if err := s.buildFromType(sig.Results().At(0).Type(), schemaTypable{&ps, 0}); err != nil {
+ return err
+ }
+ if sfName, isStrfmt := strfmtName(afld.Doc); isStrfmt {
+ ps.Typed("string", sfName)
+ ps.Ref = spec.Ref{}
+ ps.Items = nil
+ }
- return nil
+ if err := s.createParser(name, tgt.Schema(), &ps, afld).Parse(afld.Doc); err != nil {
+ return err
}
- default:
- panic(fmt.Sprintf("WARNING: can't determine refined type %s (%T)", titpe.String(), titpe))
+
+ if ps.Ref.String() == "" && name != fld.Name() {
+ ps.AddExtension("x-go-name", fld.Name())
+ }
+
+ if s.ctx.app.setXNullableForPointers {
+ if _, isPointer := fld.Type().(*types.Signature).Results().At(0).Type().(*types.Pointer); isPointer && (ps.Extensions == nil || (ps.Extensions["x-nullable"] == nil && ps.Extensions["x-isnullable"] == nil)) {
+ ps.AddExtension("x-nullable", true)
+ }
+ }
+
+ // seen[name] = fld.Name()
+ tgt.Schema().Properties[name] = ps
+ }
+
+ return nil
+}
+
+// buildAlias builds a reference to an alias from another type.
+func (s *schemaBuilder) buildAlias(tpe *types.Alias, tgt swaggerTypable) error {
+ if unsupportedBuiltinType(tpe) {
+ log.Printf("WARNING: skipped unsupported builtin type: %v", tpe)
+
+ return nil
+ }
+
+ o := tpe.Obj()
+ if isAny(o) {
+ _ = tgt.Schema()
+ return nil
+ }
+ mustNotBeABuiltinType(o)
+
+ decl, ok := s.ctx.FindModel(o.Pkg().Path(), o.Name())
+ if !ok {
+ return fmt.Errorf("can't find source file for aliased type: %v", tpe)
+ }
+
+ return s.makeRef(decl, tgt)
+}
+
+func (s *schemaBuilder) buildFromMap(titpe *types.Map, tgt swaggerTypable) error {
+ // check if key is a string type, or knows how to marshall to text.
+ // If not, print a message and skip the map property.
+ //
+ // Only maps with string keys can go into additional properties
+
+ sch := tgt.Schema()
+ if sch == nil {
+ return errors.New("items doesn't support maps")
+ }
+
+ eleProp := schemaTypable{sch, tgt.Level()}
+ key := titpe.Key()
+ if key.Underlying().String() == "string" || isTextMarshaler(key) {
+ return s.buildFromType(titpe.Elem(), eleProp.AdditionalProperties())
}
return nil
@@ -482,6 +795,7 @@ func (s *schemaBuilder) buildFromType(tpe types.Type, tgt swaggerTypable) error
func (s *schemaBuilder) buildFromInterface(decl *entityDecl, it *types.Interface, schema *spec.Schema, seen map[string]string) error {
if it.Empty() {
+ // return an empty schema for empty interfaces
return nil
}
@@ -494,98 +808,91 @@ func (s *schemaBuilder) buildFromInterface(decl *entityDecl, it *types.Interface
if specType, ok := decl.Spec.Type.(*ast.InterfaceType); ok {
flist = make([]*ast.Field, it.NumEmbeddeds()+it.NumExplicitMethods())
copy(flist, specType.Methods.List)
- // for i := range specType.Methods.List {
- // flist[i] = specType.Methods.List[i]
- // }
}
// First collect the embedded interfaces
- // create refs when the embedded interface is decorated with an allOf annotation
- for i := 0; i < it.NumEmbeddeds(); i++ {
+ // create refs when:
+ //
+ // 1. the embedded interface is decorated with an allOf annotation
+ // 2. the embedded interface is an alias
+ for i := range it.NumEmbeddeds() {
fld := it.EmbeddedType(i)
+ debugLog("inspecting embedded type in interface: %v", fld)
+ var (
+ fieldHasAllOf bool
+ err error
+ )
+
+ if tgt == nil {
+ tgt = &spec.Schema{}
+ }
switch ftpe := fld.(type) {
case *types.Named:
+ debugLog("embedded named type (buildInterface): %v", ftpe)
o := ftpe.Obj()
- var afld *ast.Field
- for _, an := range flist {
- if len(an.Names) != 0 {
- continue
- }
-
- tpp := decl.Pkg.TypesInfo.Types[an.Type]
- if tpp.Type.String() != o.Type().String() {
- continue
- }
-
- // decl.
- debugLog("maybe interface field %s: %s(%T)", o.Name(), o.Type().String(), o.Type())
- afld = an
- break
- }
-
- if afld == nil {
- debugLog("can't find source associated with %s for %s", fld.String(), it.String())
+ if isAny(o) || isStdError(o) {
+ // ignore bultin interfaces
continue
}
- // if the field is annotated with swagger:ignore, ignore it
- if ignored(afld.Doc) {
- continue
+ if fieldHasAllOf, err = s.buildNamedInterface(ftpe, flist, decl, schema, seen); err != nil {
+ return err
}
-
- if !allOfMember(afld.Doc) {
- var newSch spec.Schema
- if err := s.buildEmbedded(o.Type(), &newSch, seen); err != nil {
- return err
- }
- schema.AllOf = append(schema.AllOf, newSch)
- hasAllOf = true
- continue
+ case *types.Interface:
+ debugLog("embedded anonymous interface type (buildInterface): %v", ftpe) // e.g. type X interface{ interface{Error() string}}
+ var aliasedSchema spec.Schema
+ ps := schemaTypable{schema: &aliasedSchema}
+ if err = s.buildAnonymousInterface(ftpe, ps, decl); err != nil {
+ return err
}
- hasAllOf = true
- if tgt == nil {
- tgt = &spec.Schema{}
+ if aliasedSchema.Ref.String() != "" || len(aliasedSchema.Properties) > 0 || len(aliasedSchema.AllOf) > 0 {
+ schema.AddToAllOf(aliasedSchema)
+ fieldHasAllOf = true
}
- var newSch spec.Schema
- // when the embedded struct is annotated with swagger:allOf it will be used as allOf property
- // otherwise the fields will just be included as normal properties
- if err := s.buildAllOf(o.Type(), &newSch); err != nil {
+ case *types.Alias:
+ debugLog("embedded alias (buildInterface): %v -> %v", ftpe, ftpe.Rhs())
+ var aliasedSchema spec.Schema
+ ps := schemaTypable{schema: &aliasedSchema}
+ if err = s.buildAlias(ftpe, ps); err != nil {
return err
}
- if afld.Doc != nil {
- for _, cmt := range afld.Doc.List {
- for _, ln := range strings.Split(cmt.Text, "\n") {
- matches := rxAllOf.FindStringSubmatch(ln)
- ml := len(matches)
- if ml > 1 {
- mv := matches[ml-1]
- if mv != "" {
- schema.AddExtension("x-class", mv)
- }
- }
- }
- }
- }
- schema.AllOf = append(schema.AllOf, newSch)
+ if aliasedSchema.Ref.String() != "" || len(aliasedSchema.Properties) > 0 || len(aliasedSchema.AllOf) > 0 {
+ schema.AddToAllOf(aliasedSchema)
+ fieldHasAllOf = true
+ }
+ case *types.Union: // e.g. type X interface{ ~uint16 | ~float32 }
+ log.Printf("WARNING: union type constraints are not supported yet %[1]v (%[1]T). Skipped", ftpe)
+ case *types.TypeParam:
+ log.Printf("WARNING: generic type parameters are not supported yet %[1]v (%[1]T). Skipped", ftpe)
+ case *types.Chan:
+ log.Printf("WARNING: channels are not supported %[1]v (%[1]T). Skipped", ftpe)
+ case *types.Signature:
+ log.Printf("WARNING: functions are not supported %[1]v (%[1]T). Skipped", ftpe)
default:
- log.Printf("WARNING: can't figure out object type for allOf named type (%T): %v", ftpe, ftpe.Underlying())
+ log.Printf(
+ "WARNING: can't figure out object type for allOf named type (%T): %v",
+ ftpe, ftpe.Underlying(),
+ )
}
- debugLog("got embedded interface: %s {%T}", fld.String(), fld)
+
+ debugLog("got embedded interface: %v {%T}, fieldHasAllOf: %t", fld, fld, fieldHasAllOf)
+ hasAllOf = hasAllOf || fieldHasAllOf
}
if tgt == nil {
tgt = schema
}
+
// We can finally build the actual schema for the struct
if tgt.Properties == nil {
tgt.Properties = make(map[string]spec.Schema)
}
tgt.Typed("object", "")
- for i := 0; i < it.NumExplicitMethods(); i++ {
+ for i := range it.NumExplicitMethods() {
fld := it.ExplicitMethod(i)
if !fld.Exported() {
continue
@@ -671,17 +978,91 @@ func (s *schemaBuilder) buildFromInterface(decl *entityDecl, it *types.Interface
if hasAllOf && len(tgt.Properties) > 0 {
schema.AllOf = append(schema.AllOf, *tgt)
}
+
for k := range tgt.Properties {
if _, ok := seen[k]; !ok {
delete(tgt.Properties, k)
}
}
+
return nil
}
+func (s *schemaBuilder) buildNamedInterface(ftpe *types.Named, flist []*ast.Field, decl *entityDecl, schema *spec.Schema, seen map[string]string) (hasAllOf bool, err error) {
+ o := ftpe.Obj()
+ var afld *ast.Field
+
+ for _, an := range flist {
+ if len(an.Names) != 0 {
+ continue
+ }
+
+ tpp := decl.Pkg.TypesInfo.Types[an.Type]
+ if tpp.Type.String() != o.Type().String() {
+ continue
+ }
+
+ // decl.
+ debugLog("maybe interface field %s: %s(%T)", o.Name(), o.Type().String(), o.Type())
+ afld = an
+ break
+ }
+
+ if afld == nil {
+ debugLog("can't find source associated with %s", ftpe.String())
+ return hasAllOf, nil
+ }
+
+ // if the field is annotated with swagger:ignore, ignore it
+ if ignored(afld.Doc) {
+ return hasAllOf, nil
+ }
+
+ if !allOfMember(afld.Doc) {
+ var newSch spec.Schema
+ if err = s.buildEmbedded(o.Type(), &newSch, seen); err != nil {
+ return hasAllOf, err
+ }
+ schema.AllOf = append(schema.AllOf, newSch)
+ hasAllOf = true
+
+ return hasAllOf, nil
+ }
+
+ hasAllOf = true
+
+ var newSch spec.Schema
+ // when the embedded struct is annotated with swagger:allOf it will be used as allOf property
+ // otherwise the fields will just be included as normal properties
+ if err = s.buildAllOf(o.Type(), &newSch); err != nil {
+ return hasAllOf, err
+ }
+
+ if afld.Doc != nil {
+ for _, cmt := range afld.Doc.List {
+ for _, ln := range strings.Split(cmt.Text, "\n") {
+ matches := rxAllOf.FindStringSubmatch(ln)
+ ml := len(matches)
+ if ml <= 1 {
+ continue
+ }
+
+ mv := matches[ml-1]
+ if mv != "" {
+ schema.AddExtension("x-class", mv)
+ }
+ }
+ }
+ }
+
+ schema.AllOf = append(schema.AllOf, newSch)
+
+ return hasAllOf, nil
+}
+
func (s *schemaBuilder) buildFromStruct(decl *entityDecl, st *types.Struct, schema *spec.Schema, seen map[string]string) error {
- s.ctx.FindComments(decl.Pkg, decl.Type.Obj().Name())
- cmt, hasComments := s.ctx.FindComments(decl.Pkg, decl.Type.Obj().Name())
+ s.ctx.FindComments(decl.Pkg, decl.Obj().Name())
+ cmt, hasComments := s.ctx.FindComments(decl.Pkg, decl.Obj().Name())
if !hasComments {
cmt = new(ast.CommentGroup)
}
@@ -693,15 +1074,19 @@ func (s *schemaBuilder) buildFromStruct(decl *entityDecl, st *types.Struct, sche
var tgt *spec.Schema
hasAllOf := false
- for i := 0; i < st.NumFields(); i++ {
+ for i := range st.NumFields() {
fld := st.Field(i)
if !fld.Anonymous() {
+ // e.g. struct { _ struct{} }
debugLog("skipping field %q for allOf scan because not anonymous", fld.Name())
continue
}
tg := st.Tag(i)
- debugLog("maybe allof field(%t) %s: %s (%T) [%q](anon: %t, embedded: %t)", fld.IsField(), fld.Name(), fld.Type().String(), fld.Type(), tg, fld.Anonymous(), fld.Embedded())
+ debugLog(
+ "maybe allof field(%t) %s: %s (%T) [%q](anon: %t, embedded: %t)",
+ fld.IsField(), fld.Name(), fld.Type().String(), fld.Type(), tg, fld.Anonymous(), fld.Embedded(),
+ )
var afld *ast.Field
ans, _ := astutil.PathEnclosingInterval(decl.File, fld.Pos(), fld.Pos())
// debugLog("got %d nodes (exact: %t)", len(ans), isExact)
@@ -734,7 +1119,9 @@ func (s *schemaBuilder) buildFromStruct(decl *entityDecl, st *types.Struct, sche
continue
}
- if !allOfMember(afld.Doc) {
+ _, isAliased := fld.Type().(*types.Alias)
+
+ if !allOfMember(afld.Doc) && !isAliased {
if tgt == nil {
tgt = schema
}
@@ -743,6 +1130,11 @@ func (s *schemaBuilder) buildFromStruct(decl *entityDecl, st *types.Struct, sche
}
continue
}
+
+ if isAliased {
+ debugLog("alias member in struct: %v", fld)
+ }
+
// if this created an allOf property then we have to rejig the schema var
// because all the fields collected that aren't from embedded structs should go in
// their own proper schema
@@ -789,7 +1181,7 @@ func (s *schemaBuilder) buildFromStruct(decl *entityDecl, st *types.Struct, sche
}
tgt.Typed("object", "")
- for i := 0; i < st.NumFields(); i++ {
+ for i := range st.NumFields() {
fld := st.Field(i)
tg := st.Tag(i)
@@ -804,7 +1196,6 @@ func (s *schemaBuilder) buildFromStruct(decl *entityDecl, st *types.Struct, sche
var afld *ast.Field
ans, _ := astutil.PathEnclosingInterval(decl.File, fld.Pos(), fld.Pos())
- // debugLog("got %d nodes (exact: %t)", len(ans), isExact)
for _, an := range ans {
at, valid := an.(*ast.Field)
if !valid {
@@ -894,79 +1285,174 @@ func (s *schemaBuilder) buildFromStruct(decl *entityDecl, st *types.Struct, sche
func (s *schemaBuilder) buildAllOf(tpe types.Type, schema *spec.Schema) error {
debugLog("allOf %s", tpe.Underlying())
+
switch ftpe := tpe.(type) {
case *types.Pointer:
return s.buildAllOf(ftpe.Elem(), schema)
case *types.Named:
- switch utpe := ftpe.Underlying().(type) {
- case *types.Struct:
- decl, found := s.ctx.FindModel(ftpe.Obj().Pkg().Path(), ftpe.Obj().Name())
- if found {
- if ftpe.Obj().Pkg().Path() == "time" && ftpe.Obj().Name() == "Time" {
- schema.Typed("string", "date-time")
- return nil
- }
- if sfnm, isf := strfmtName(decl.Comments); isf {
- schema.Typed("string", sfnm)
- return nil
- }
- if decl.HasModelAnnotation() {
- return s.makeRef(decl, schemaTypable{schema, 0})
- }
- return s.buildFromStruct(decl, utpe, schema, make(map[string]string))
- }
+ return s.buildNamedAllOf(ftpe, schema)
+ case *types.Alias:
+ debugLog("allOf member is alias %v => %v", ftpe, ftpe.Rhs())
+ tgt := schemaTypable{schema: schema}
+ return s.buildAlias(ftpe, tgt)
+ case *types.TypeParam:
+ log.Printf("WARNING: generic type parameters are not supported yet %[1]v (%[1]T). Skipped", ftpe)
+ return nil
+ case *types.Chan:
+ log.Printf("WARNING: channels are not supported %[1]v (%[1]T). Skipped", ftpe)
+ return nil
+ case *types.Signature:
+ log.Printf("WARNING: functions are not supported %[1]v (%[1]T). Skipped", ftpe)
+ return nil
+ default:
+ log.Printf("WARNING: missing allOf parser for a %T, skipping field", ftpe)
+ return fmt.Errorf("unable to resolve allOf member for: %v", ftpe)
+ }
+}
+
+func (s *schemaBuilder) buildNamedAllOf(ftpe *types.Named, schema *spec.Schema) error {
+ switch utpe := ftpe.Underlying().(type) {
+ case *types.Struct:
+ decl, found := s.ctx.FindModel(ftpe.Obj().Pkg().Path(), ftpe.Obj().Name())
+ if !found {
return fmt.Errorf("can't find source file for struct: %s", ftpe.String())
- case *types.Interface:
- decl, found := s.ctx.FindModel(ftpe.Obj().Pkg().Path(), ftpe.Obj().Name())
- if found {
- if sfnm, isf := strfmtName(decl.Comments); isf {
- schema.Typed("string", sfnm)
- return nil
- }
- if decl.HasModelAnnotation() {
- return s.makeRef(decl, schemaTypable{schema, 0})
- }
- return s.buildFromInterface(decl, utpe, schema, make(map[string]string))
- }
+ }
+
+ if isStdTime(ftpe.Obj()) {
+ schema.Typed("string", "date-time")
+ return nil
+ }
+
+ if sfnm, isf := strfmtName(decl.Comments); isf {
+ schema.Typed("string", sfnm)
+ return nil
+ }
+
+ if decl.HasModelAnnotation() {
+ return s.makeRef(decl, schemaTypable{schema, 0})
+ }
+
+ return s.buildFromStruct(decl, utpe, schema, make(map[string]string))
+ case *types.Interface:
+ decl, found := s.ctx.FindModel(ftpe.Obj().Pkg().Path(), ftpe.Obj().Name())
+ if !found {
return fmt.Errorf("can't find source file for interface: %s", ftpe.String())
- default:
- log.Printf("WARNING: can't figure out object type for allOf named type (%T): %v", ftpe, ftpe.Underlying())
- return fmt.Errorf("unable to locate source file for allOf %s", utpe.String())
}
+
+ if sfnm, isf := strfmtName(decl.Comments); isf {
+ schema.Typed("string", sfnm)
+ return nil
+ }
+
+ if decl.HasModelAnnotation() {
+ return s.makeRef(decl, schemaTypable{schema, 0})
+ }
+
+ return s.buildFromInterface(decl, utpe, schema, make(map[string]string))
+ case *types.TypeParam:
+ log.Printf("WARNING: generic type parameters are not supported yet %[1]v (%[1]T). Skipped", ftpe)
+ return nil
+ case *types.Chan:
+ log.Printf("WARNING: channels are not supported %[1]v (%[1]T). Skipped", ftpe)
+ return nil
+ case *types.Signature:
+ log.Printf("WARNING: functions are not supported %[1]v (%[1]T). Skipped", ftpe)
+ return nil
default:
- log.Printf("WARNING: Missing allOf parser for a %T, skipping field", ftpe)
- return fmt.Errorf("unable to resolve allOf member for: %v", ftpe)
+ log.Printf(
+ "WARNING: can't figure out object type for allOf named type (%T): %v",
+ ftpe, utpe,
+ )
+ return fmt.Errorf("unable to locate source file for allOf (%T): %v",
+ ftpe, utpe,
+ )
}
}
func (s *schemaBuilder) buildEmbedded(tpe types.Type, schema *spec.Schema, seen map[string]string) error {
- debugLog("embedded %s", tpe.Underlying())
+ debugLog("embedded %v", tpe.Underlying())
+
switch ftpe := tpe.(type) {
case *types.Pointer:
return s.buildEmbedded(ftpe.Elem(), schema, seen)
case *types.Named:
- debugLog("embedded named type: %T", ftpe.Underlying())
- switch utpe := ftpe.Underlying().(type) {
- case *types.Struct:
- decl, found := s.ctx.FindModel(ftpe.Obj().Pkg().Path(), ftpe.Obj().Name())
- if found {
- return s.buildFromStruct(decl, utpe, schema, seen)
- }
+ return s.buildNamedEmbedded(ftpe, schema, seen)
+ case *types.Alias:
+ debugLog("embedded alias %v => %v", ftpe, ftpe.Rhs())
+ tgt := schemaTypable{schema, 0}
+ return s.buildAlias(ftpe, tgt)
+ case *types.Union: // e.g. type X interface{ ~uint16 | ~float32 }
+ log.Printf("WARNING: union type constraints are not supported yet %[1]v (%[1]T). Skipped", ftpe)
+ return nil
+ case *types.TypeParam:
+ log.Printf("WARNING: generic type parameters are not supported yet %[1]v (%[1]T). Skipped", ftpe)
+ return nil
+ case *types.Chan:
+ log.Printf("WARNING: channels are not supported %[1]v (%[1]T). Skipped", ftpe)
+ return nil
+ case *types.Signature:
+ log.Printf("WARNING: functions are not supported %[1]v (%[1]T). Skipped", ftpe)
+ return nil
+ default:
+ log.Printf("WARNING: Missing embedded parser for a %T, skipping model\n", ftpe)
+ return nil
+ }
+}
+
+func (s *schemaBuilder) buildNamedEmbedded(ftpe *types.Named, schema *spec.Schema, seen map[string]string) error {
+ debugLog("embedded named type: %T", ftpe.Underlying())
+ if unsupportedBuiltin(ftpe) {
+ log.Printf("WARNING: skipped unsupported builtin type: %v", ftpe)
+
+ return nil
+ }
+
+ switch utpe := ftpe.Underlying().(type) {
+ case *types.Struct:
+ decl, found := s.ctx.FindModel(ftpe.Obj().Pkg().Path(), ftpe.Obj().Name())
+ if !found {
return fmt.Errorf("can't find source file for struct: %s", ftpe.String())
- case *types.Interface:
- decl, found := s.ctx.FindModel(ftpe.Obj().Pkg().Path(), ftpe.Obj().Name())
- if found {
- return s.buildFromInterface(decl, utpe, schema, seen)
- }
+ }
+
+ return s.buildFromStruct(decl, utpe, schema, seen)
+ case *types.Interface:
+ if utpe.Empty() {
+ return nil
+ }
+ o := ftpe.Obj()
+ if isAny(o) {
+ return nil
+ }
+ if isStdError(o) {
+ tgt := schemaTypable{schema: schema}
+ tgt.AddExtension("x-go-type", o.Name())
+ return swaggerSchemaForType(o.Name(), tgt)
+ }
+ mustNotBeABuiltinType(o)
+
+ decl, found := s.ctx.FindModel(o.Pkg().Path(), o.Name())
+ if !found {
return fmt.Errorf("can't find source file for struct: %s", ftpe.String())
- default:
- log.Printf("WARNING: can't figure out object type for embedded named type (%T): %v", ftpe, ftpe.Underlying())
}
+ return s.buildFromInterface(decl, utpe, schema, seen)
+ case *types.Union: // e.g. type X interface{ ~uint16 | ~float32 }
+ log.Printf("WARNING: union type constraints are not supported yet %[1]v (%[1]T). Skipped", utpe)
+ return nil
+ case *types.TypeParam:
+ log.Printf("WARNING: generic type parameters are not supported yet %[1]v (%[1]T). Skipped", utpe)
+ return nil
+ case *types.Chan:
+ log.Printf("WARNING: channels are not supported %[1]v (%[1]T). Skipped", utpe)
+ return nil
+ case *types.Signature:
+ log.Printf("WARNING: functions are not supported %[1]v (%[1]T). Skipped", utpe)
+ return nil
default:
- log.Printf("WARNING: Missing embedded parser for a %T, skipping model\n", ftpe)
+ log.Printf("WARNING: can't figure out object type for embedded named type (%T): %v",
+ ftpe, utpe,
+ )
return nil
}
- return nil
}
func (s *schemaBuilder) makeRef(decl *entityDecl, prop swaggerTypable) error {
@@ -991,7 +1477,7 @@ func (s *schemaBuilder) createParser(nm string, schema, ps *spec.Schema, fld *as
if ps.Ref.String() == "" {
sp.setDescription = func(lines []string) {
ps.Description = joinDropLast(lines)
- enumDesc := getEnumDesc(ps.VendorExtensible.Extensions)
+ enumDesc := getEnumDesc(ps.Extensions)
if enumDesc != "" {
ps.Description += "\n" + enumDesc
}
@@ -1070,7 +1556,7 @@ func (s *schemaBuilder) createParser(nm string, schema, ps *spec.Schema, fld *as
}
return otherTaggers, nil
default:
- return nil, fmt.Errorf("unknown field type ele for %q", nm)
+ return nil, fmt.Errorf("unknown field type element for %q", nm)
}
}
// check if this is a primitive, if so parse the validations from the
@@ -1178,3 +1664,29 @@ func isFieldStringable(tpe ast.Expr) bool {
}
return false
}
+
+func isTextMarshaler(tpe types.Type) bool {
+ encodingPkg, err := importer.Default().Import("encoding")
+ if err != nil {
+ return false
+ }
+ ifc := encodingPkg.Scope().Lookup("TextMarshaler").Type().Underlying().(*types.Interface) // TODO: there is a better way to check this
+
+ return types.Implements(tpe, ifc)
+}
+
+func isStdTime(o *types.TypeName) bool {
+ return o.Pkg() != nil && o.Pkg().Name() == "time" && o.Name() == "Time"
+}
+
+func isStdError(o *types.TypeName) bool {
+ return o.Pkg() == nil && o.Name() == "error"
+}
+
+func isStdJSONRawMessage(o *types.TypeName) bool {
+ return o.Pkg() != nil && o.Pkg().Path() == "encoding/json" && o.Name() == "RawMessage"
+}
+
+func isAny(o *types.TypeName) bool {
+ return o.Pkg() == nil && o.Name() == "any"
+}