github.com/AngusLu/go-swagger@v0.28.0/generator/types.go (about)

     1  // Copyright 2015 go-swagger maintainers
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //    http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package generator
    16  
    17  import (
    18  	"fmt"
    19  	"log"
    20  	"path/filepath"
    21  	"reflect"
    22  	"strings"
    23  
    24  	"github.com/go-openapi/loads"
    25  	"github.com/go-openapi/spec"
    26  	"github.com/go-openapi/swag"
    27  	"github.com/kr/pretty"
    28  	"github.com/mitchellh/mapstructure"
    29  )
    30  
    31  const (
    32  	iface   = "interface{}"
    33  	array   = "array"
    34  	file    = "file"
    35  	number  = "number"
    36  	integer = "integer"
    37  	boolean = "boolean"
    38  	str     = "string"
    39  	object  = "object"
    40  	binary  = "binary"
    41  	body    = "body"
    42  	b64     = "byte"
    43  )
    44  
    45  // Extensions supported by go-swagger
    46  const (
    47  	xClass        = "x-class"         // class name used by discriminator
    48  	xGoCustomTag  = "x-go-custom-tag" // additional tag for serializers on struct fields
    49  	xGoName       = "x-go-name"       // name of the generated go variable
    50  	xGoType       = "x-go-type"       // reuse existing type (do not generate)
    51  	xIsNullable   = "x-isnullable"
    52  	xNullable     = "x-nullable" // turns the schema into a pointer
    53  	xOmitEmpty    = "x-omitempty"
    54  	xSchemes      = "x-schemes" // additional schemes supported for operations (server generation)
    55  	xOrder        = "x-order"   // sort order for properties (or any schema)
    56  	xGoJSONString = "x-go-json-string"
    57  	xGoEnumCI     = "x-go-enum-ci" // make string enumeration case-insensitive
    58  
    59  	xGoOperationTag = "x-go-operation-tag" // additional tag to override generation in operation groups
    60  )
    61  
    62  // swaggerTypeName contains a mapping from go type to swagger type or format
    63  var swaggerTypeName map[string]string
    64  
    65  func initTypes() {
    66  	swaggerTypeName = make(map[string]string)
    67  	for k, v := range typeMapping {
    68  		swaggerTypeName[v] = k
    69  	}
    70  }
    71  
    72  func simpleResolvedType(tn, fmt string, items *spec.Items, v *spec.CommonValidations) (result resolvedType) {
    73  	result.SwaggerType = tn
    74  	result.SwaggerFormat = fmt
    75  
    76  	defer func() {
    77  		guardValidations(result.SwaggerType, v)
    78  	}()
    79  
    80  	if tn == file {
    81  		// special case of swagger type "file", rendered as io.ReadCloser interface
    82  		result.IsPrimitive = true
    83  		result.GoType = formatMapping[str][binary]
    84  		result.IsStream = true
    85  		return
    86  	}
    87  
    88  	if fmt != "" {
    89  		defer func() {
    90  			guardFormatConflicts(result.SwaggerFormat, v)
    91  		}()
    92  
    93  		fmtn := strings.ReplaceAll(fmt, "-", "")
    94  		if fmm, ok := formatMapping[tn]; ok {
    95  			if tpe, ok := fmm[fmtn]; ok {
    96  				result.GoType = tpe
    97  				result.IsPrimitive = true
    98  				_, result.IsCustomFormatter = customFormatters[tpe]
    99  				// special case of swagger format "binary", rendered as io.ReadCloser interface
   100  				// TODO(fredbi): should set IsCustomFormatter=false when binary
   101  				result.IsStream = fmt == binary
   102  				// special case of swagger format "byte", rendered as a strfmt.Base64 type: no validation
   103  				result.IsBase64 = fmt == b64
   104  				return
   105  			}
   106  		}
   107  	}
   108  
   109  	if tpe, ok := typeMapping[tn]; ok {
   110  		result.GoType = tpe
   111  		_, result.IsPrimitive = primitives[tpe]
   112  		result.IsPrimitive = ok
   113  		return
   114  	}
   115  
   116  	if tn == array {
   117  		result.IsArray = true
   118  		result.IsPrimitive = false
   119  		result.IsCustomFormatter = false
   120  		result.IsNullable = false
   121  		if items == nil {
   122  			result.GoType = "[]" + iface
   123  			return
   124  		}
   125  		res := simpleResolvedType(items.Type, items.Format, items.Items, &items.CommonValidations)
   126  		result.GoType = "[]" + res.GoType
   127  		return
   128  	}
   129  	result.GoType = tn
   130  	_, result.IsPrimitive = primitives[tn]
   131  	return
   132  }
   133  
   134  func newTypeResolver(pkg, fullPkg string, doc *loads.Document) *typeResolver {
   135  	resolver := typeResolver{ModelsPackage: pkg, Doc: doc}
   136  	resolver.KnownDefs = make(map[string]struct{}, len(doc.Spec().Definitions))
   137  	for k, sch := range doc.Spec().Definitions {
   138  		tpe, _, _ := resolver.knownDefGoType(k, sch, nil)
   139  		resolver.KnownDefs[tpe] = struct{}{}
   140  	}
   141  	return &resolver
   142  }
   143  
   144  // knownDefGoType returns go type, package and package alias for definition
   145  func (t typeResolver) knownDefGoType(def string, schema spec.Schema, clear func(string) string) (string, string, string) {
   146  	debugLog("known def type: %q", def)
   147  	ext := schema.Extensions
   148  	nm, hasGoName := ext.GetString(xGoName)
   149  
   150  	if hasGoName {
   151  		debugLog("known def type %s named from %s as %q", def, xGoName, nm)
   152  		def = nm
   153  	}
   154  	extType, isExternalType := t.resolveExternalType(ext)
   155  	if !isExternalType || extType.Embedded {
   156  		if clear == nil {
   157  			debugLog("known def type no clear: %q", def)
   158  			return def, t.definitionPkg, ""
   159  		}
   160  		debugLog("known def type clear: %q -> %q", def, clear(def))
   161  		return clear(def), t.definitionPkg, ""
   162  	}
   163  
   164  	// external type definition trumps regular type resolution
   165  	if extType.Import.Alias == "" {
   166  		debugLog("type %s imported as external type %s, assumed in current package", def, extType.Type)
   167  		return extType.Type, extType.Import.Package, extType.Import.Alias
   168  	}
   169  	debugLog("type %s imported as external type from %s as %s.%s", def, extType.Import.Package, extType.Import.Alias, extType.Type)
   170  	return extType.Import.Alias + "." + extType.Type, extType.Import.Package, extType.Import.Alias
   171  }
   172  
   173  // x-go-type:
   174  //   type: mytype
   175  //   import:
   176  //     package:
   177  //     alias:
   178  //   hints:
   179  //     kind: map|object|array|interface|primitive|stream|tuple
   180  //     nullable: true|false
   181  //  embedded: true
   182  type externalTypeDefinition struct {
   183  	Type   string
   184  	Import struct {
   185  		Package string
   186  		Alias   string
   187  	}
   188  	Hints struct {
   189  		Kind         string
   190  		Nullable     *bool
   191  		NoValidation *bool
   192  	}
   193  	Embedded bool
   194  }
   195  
   196  func hasExternalType(ext spec.Extensions) (*externalTypeDefinition, bool) {
   197  	v, ok := ext[xGoType]
   198  	if !ok {
   199  		return nil, false
   200  	}
   201  
   202  	var extType externalTypeDefinition
   203  	err := mapstructure.Decode(v, &extType)
   204  	if err != nil {
   205  		log.Printf("warning: x-go-type extension could not be decoded (%v). Skipped", v)
   206  		return nil, false
   207  	}
   208  
   209  	return &extType, true
   210  }
   211  
   212  func (t typeResolver) resolveExternalType(ext spec.Extensions) (*externalTypeDefinition, bool) {
   213  	extType, hasExt := hasExternalType(ext)
   214  	if !hasExt {
   215  		return nil, false
   216  	}
   217  
   218  	// NOTE:
   219  	// * basic deconfliction of the default alias
   220  	// * if no package is specified, defaults to models (as provided from CLI or defaut generation location for models)
   221  	toAlias := func(pkg string) string {
   222  		mangled := GoLangOpts().ManglePackageName(pkg, "")
   223  		return deconflictPkg(mangled, func(in string) string {
   224  			return in + "ext"
   225  		})
   226  	}
   227  
   228  	switch {
   229  	case extType.Import.Package != "" && extType.Import.Alias == "":
   230  		extType.Import.Alias = toAlias(extType.Import.Package)
   231  	case extType.Import.Package == "" && extType.Import.Alias != "":
   232  		extType.Import.Package = t.ModelsFullPkg
   233  	case extType.Import.Package == "" && extType.Import.Alias == "":
   234  		// in this case, the external type is assumed to be present in the current package.
   235  		// For completion, whenever this type is used in anonymous types declared by operations,
   236  		// we assume this is the package where models are expected to be found.
   237  		extType.Import.Package = t.ModelsFullPkg
   238  		if extType.Import.Package != "" {
   239  			extType.Import.Alias = toAlias(extType.Import.Package)
   240  		}
   241  	}
   242  
   243  	debugLogAsJSON("known def external %s type", xGoType, extType)
   244  
   245  	return extType, true
   246  }
   247  
   248  type typeResolver struct {
   249  	Doc           *loads.Document
   250  	ModelsPackage string // package alias (e.g. "models")
   251  	ModelsFullPkg string // fully qualified package (e.g. "github.com/example/models")
   252  	ModelName     string
   253  	KnownDefs     map[string]struct{}
   254  	// unexported fields
   255  	keepDefinitionsPkg string
   256  	knownDefsKept      map[string]struct{}
   257  	definitionPkg      string // pkg alias to fill in GenSchema.Pkg
   258  }
   259  
   260  // NewWithModelName clones a type resolver and specifies a new model name
   261  func (t *typeResolver) NewWithModelName(name string) *typeResolver {
   262  	tt := newTypeResolver(t.ModelsPackage, t.ModelsFullPkg, t.Doc)
   263  	tt.ModelName = name
   264  
   265  	// propagates kept definitions
   266  	tt.keepDefinitionsPkg = t.keepDefinitionsPkg
   267  	tt.knownDefsKept = t.knownDefsKept
   268  	tt.definitionPkg = t.definitionPkg
   269  	return tt
   270  }
   271  
   272  // withKeepDefinitionsPackage instructs the type resolver to keep previously resolved package name for
   273  // definitions known at the moment it is first called.
   274  func (t *typeResolver) withKeepDefinitionsPackage(definitionsPackage string) *typeResolver {
   275  	t.keepDefinitionsPkg = definitionsPackage
   276  	t.knownDefsKept = make(map[string]struct{}, len(t.KnownDefs))
   277  	for k := range t.KnownDefs {
   278  		t.knownDefsKept[k] = struct{}{}
   279  	}
   280  	return t
   281  }
   282  
   283  // withDefinitionPackage sets the definition pkg that object/struct types to be generated
   284  // in GenSchema.Pkg field.
   285  // ModelsPackage field can not replace definitionPkg since ModelsPackage will be prepend to .GoType,
   286  // while definitionPkg is just used to fill the .Pkg in GenSchema
   287  func (t *typeResolver) withDefinitionPackage(pkg string) *typeResolver {
   288  	t.definitionPkg = pkg
   289  	return t
   290  }
   291  
   292  func (t *typeResolver) resolveSchemaRef(schema *spec.Schema, isRequired bool) (returns bool, result resolvedType, err error) {
   293  	if schema.Ref.String() == "" {
   294  		return
   295  	}
   296  	debugLog("resolving ref (anon: %t, req: %t) %s", false, isRequired, schema.Ref.String())
   297  
   298  	returns = true
   299  	var ref *spec.Schema
   300  	var er error
   301  
   302  	ref, er = spec.ResolveRef(t.Doc.Spec(), &schema.Ref)
   303  	if er != nil {
   304  		debugLog("error resolving ref %s: %v", schema.Ref.String(), er)
   305  		err = er
   306  		return
   307  	}
   308  
   309  	extType, isExternalType := t.resolveExternalType(schema.Extensions)
   310  	if isExternalType {
   311  		// deal with validations for an aliased external type
   312  		result.SkipExternalValidation = swag.BoolValue(extType.Hints.NoValidation)
   313  	}
   314  
   315  	res, er := t.ResolveSchema(ref, false, isRequired)
   316  	if er != nil {
   317  		err = er
   318  		return
   319  	}
   320  	result = res
   321  
   322  	tn := filepath.Base(schema.Ref.GetURL().Fragment)
   323  	tpe, pkg, alias := t.knownDefGoType(tn, *ref, t.goTypeName)
   324  	debugLog("type name %s, package %s, alias %s", tpe, pkg, alias)
   325  	if tpe != "" {
   326  		result.GoType = tpe
   327  		result.Pkg = pkg
   328  		result.PkgAlias = alias
   329  	}
   330  	result.HasDiscriminator = res.HasDiscriminator
   331  	result.IsBaseType = result.HasDiscriminator
   332  	result.IsNullable = result.IsNullable || t.isNullable(ref) // this has to be overriden for slices and maps
   333  	result.IsEnumCI = false
   334  	return
   335  }
   336  
   337  func (t *typeResolver) inferAliasing(result *resolvedType, schema *spec.Schema, isAnonymous bool, isRequired bool) {
   338  	if !isAnonymous && t.ModelName != "" {
   339  		result.AliasedType = result.GoType
   340  		result.IsAliased = true
   341  		result.GoType = t.goTypeName(t.ModelName)
   342  		result.Pkg = t.definitionPkg
   343  	}
   344  }
   345  
   346  func (t *typeResolver) resolveFormat(schema *spec.Schema, isAnonymous bool, isRequired bool) (returns bool, result resolvedType, err error) {
   347  
   348  	if schema.Format != "" {
   349  		// defaults to string
   350  		result.SwaggerType = str
   351  		if len(schema.Type) > 0 {
   352  			result.SwaggerType = schema.Type[0]
   353  		}
   354  
   355  		debugLog("resolving format (anon: %t, req: %t)", isAnonymous, isRequired)
   356  		schFmt := strings.ReplaceAll(schema.Format, "-", "")
   357  		if fmm, ok := formatMapping[result.SwaggerType]; ok {
   358  			if tpe, ok := fmm[schFmt]; ok {
   359  				returns = true
   360  				result.GoType = tpe
   361  				_, result.IsCustomFormatter = customFormatters[tpe]
   362  			}
   363  		}
   364  		if tpe, ok := typeMapping[schFmt]; !returns && ok {
   365  			returns = true
   366  			result.GoType = tpe
   367  			_, result.IsCustomFormatter = customFormatters[tpe]
   368  		}
   369  
   370  		result.SwaggerFormat = schema.Format
   371  		t.inferAliasing(&result, schema, isAnonymous, isRequired)
   372  		// special case of swagger format "binary", rendered as io.ReadCloser interface and is therefore not a primitive type
   373  		// TODO: should set IsCustomFormatter=false in this case.
   374  		result.IsPrimitive = schFmt != binary
   375  		result.IsStream = schFmt == binary
   376  		result.IsBase64 = schFmt == b64
   377  		// propagate extensions in resolvedType
   378  		result.Extensions = schema.Extensions
   379  
   380  		switch result.SwaggerType {
   381  		case str:
   382  			result.IsNullable = nullableStrfmt(schema, isRequired)
   383  		case number, integer:
   384  			result.IsNullable = nullableNumber(schema, isRequired)
   385  		default:
   386  			result.IsNullable = t.isNullable(schema)
   387  		}
   388  	}
   389  
   390  	guardFormatConflicts(schema.Format, schema)
   391  	return
   392  }
   393  
   394  // isNullable hints the generator as to render the type with a pointer or not.
   395  //
   396  // A schema is deemed nullable (i.e. rendered by a pointer) when:
   397  // - a custom extension says it has to be so
   398  // - it is an object with properties
   399  // - it is a composed object (allOf)
   400  //
   401  // The interpretation of Required as a mean to make a type nullable is carried out elsewhere.
   402  func (t *typeResolver) isNullable(schema *spec.Schema) bool {
   403  
   404  	if nullable, ok := t.isNullableOverride(schema); ok {
   405  		return nullable
   406  	}
   407  
   408  	return len(schema.Properties) > 0 || len(schema.AllOf) > 0
   409  }
   410  
   411  // isNullableOverride determines a nullable flag forced by an extension
   412  func (t *typeResolver) isNullableOverride(schema *spec.Schema) (bool, bool) {
   413  	check := func(extension string) (bool, bool) {
   414  		v, found := schema.Extensions[extension]
   415  		nullable, cast := v.(bool)
   416  		return nullable, found && cast
   417  	}
   418  
   419  	if nullable, ok := check(xIsNullable); ok {
   420  		return nullable, ok
   421  	}
   422  
   423  	if nullable, ok := check(xNullable); ok {
   424  		return nullable, ok
   425  	}
   426  
   427  	return false, false
   428  }
   429  
   430  func (t *typeResolver) firstType(schema *spec.Schema) string {
   431  	if len(schema.Type) == 0 || schema.Type[0] == "" {
   432  		return object
   433  	}
   434  	if len(schema.Type) > 1 {
   435  		// JSON-Schema multiple types, e.g. {"type": [ "object", "array" ]} are not supported.
   436  		// TODO: should keep the first _supported_ type, e.g. skip null
   437  		log.Printf("warning: JSON-Schema type definition as array with several types is not supported in %#v. Taking the first type: %s", schema.Type, schema.Type[0])
   438  	}
   439  	return schema.Type[0]
   440  }
   441  
   442  func (t *typeResolver) resolveArray(schema *spec.Schema, isAnonymous, isRequired bool) (result resolvedType, err error) {
   443  	debugLog("resolving array (anon: %t, req: %t)", isAnonymous, isRequired)
   444  
   445  	result.IsArray = true
   446  	result.IsNullable = false
   447  
   448  	if schema.AdditionalItems != nil {
   449  		result.HasAdditionalItems = (schema.AdditionalItems.Allows || schema.AdditionalItems.Schema != nil)
   450  	}
   451  
   452  	if schema.Items == nil {
   453  		result.GoType = "[]" + iface
   454  		result.SwaggerType = array
   455  		result.SwaggerFormat = ""
   456  		t.inferAliasing(&result, schema, isAnonymous, isRequired)
   457  
   458  		return
   459  	}
   460  
   461  	if len(schema.Items.Schemas) > 0 {
   462  		result.IsArray = false
   463  		result.IsTuple = true
   464  		result.SwaggerType = array
   465  		result.SwaggerFormat = ""
   466  		t.inferAliasing(&result, schema, isAnonymous, isRequired)
   467  
   468  		return
   469  	}
   470  
   471  	rt, er := t.ResolveSchema(schema.Items.Schema, true, false)
   472  	if er != nil {
   473  		err = er
   474  		return
   475  	}
   476  
   477  	// Override the general nullability rule from ResolveSchema() in array elements:
   478  	// - only complex items are nullable (when not discriminated, not forced by x-nullable)
   479  	// - arrays of allOf have non nullable elements when not forced by x-nullable
   480  	elem := schema.Items.Schema
   481  	if elem.Ref.String() != "" {
   482  		// drill into $ref to figure out whether we want the element type to nullable or not
   483  		resolved, erf := spec.ResolveRef(t.Doc.Spec(), &elem.Ref)
   484  		if erf != nil {
   485  			debugLog("error resolving ref %s: %v", schema.Ref.String(), erf)
   486  		}
   487  		elem = resolved
   488  	}
   489  
   490  	debugLogAsJSON("resolved item for %s", rt.GoType, elem)
   491  	if nullable, ok := t.isNullableOverride(elem); ok {
   492  		debugLog("found nullable override in element %s: %t", rt.GoType, nullable)
   493  		rt.IsNullable = nullable
   494  	} else {
   495  		// this differs from isNullable for elements with AllOf
   496  		debugLog("no nullable override in element %s: Properties: %t, HasDiscriminator: %t", rt.GoType, len(elem.Properties) > 0, rt.HasDiscriminator)
   497  		rt.IsNullable = len(elem.Properties) > 0 && !rt.HasDiscriminator
   498  	}
   499  
   500  	result.GoType = "[]" + rt.GoType
   501  	if rt.IsNullable && !strings.HasPrefix(rt.GoType, "*") {
   502  		result.GoType = "[]*" + rt.GoType
   503  	}
   504  
   505  	result.ElemType = &rt
   506  	result.SwaggerType = array
   507  	result.SwaggerFormat = ""
   508  	result.IsEnumCI = hasEnumCI(schema.Extensions)
   509  	t.inferAliasing(&result, schema, isAnonymous, isRequired)
   510  	result.Extensions = schema.Extensions
   511  
   512  	return
   513  }
   514  
   515  func (t *typeResolver) goTypeName(nm string) string {
   516  	if len(t.knownDefsKept) > 0 {
   517  		// if a definitions package has been defined, already resolved definitions are
   518  		// always resolved against their original package (e.g. "models"), and not the
   519  		// current package.
   520  		// This allows complex anonymous extra schemas to reuse known definitions generated in another package.
   521  		if _, ok := t.knownDefsKept[nm]; ok {
   522  			return strings.Join([]string{t.keepDefinitionsPkg, swag.ToGoName(nm)}, ".")
   523  		}
   524  	}
   525  
   526  	if t.ModelsPackage == "" {
   527  		return swag.ToGoName(nm)
   528  	}
   529  	if _, ok := t.KnownDefs[nm]; ok {
   530  		return strings.Join([]string{t.ModelsPackage, swag.ToGoName(nm)}, ".")
   531  	}
   532  	return swag.ToGoName(nm)
   533  }
   534  
   535  func (t *typeResolver) resolveObject(schema *spec.Schema, isAnonymous bool) (result resolvedType, err error) {
   536  	debugLog("resolving object %s (anon: %t, req: %t)", t.ModelName, isAnonymous, false)
   537  
   538  	result.IsAnonymous = isAnonymous
   539  
   540  	result.IsBaseType = schema.Discriminator != ""
   541  	if !isAnonymous {
   542  		result.SwaggerType = object
   543  		tpe, pkg, alias := t.knownDefGoType(t.ModelName, *schema, t.goTypeName)
   544  		result.GoType = tpe
   545  		result.Pkg = pkg
   546  		result.PkgAlias = alias
   547  	}
   548  	if len(schema.AllOf) > 0 {
   549  		result.GoType = t.goTypeName(t.ModelName)
   550  		result.IsComplexObject = true
   551  		var isNullable bool
   552  		for _, sch := range schema.AllOf {
   553  			p := sch
   554  			if t.isNullable(&p) {
   555  				isNullable = true
   556  			}
   557  		}
   558  		if override, ok := t.isNullableOverride(schema); ok {
   559  			// prioritize x-nullable extensions
   560  			result.IsNullable = override
   561  		} else {
   562  			result.IsNullable = isNullable
   563  		}
   564  		result.SwaggerType = object
   565  		return
   566  	}
   567  
   568  	// if this schema has properties, build a map of property name to
   569  	// resolved type, this should also flag the object as anonymous,
   570  	// when a ref is found, the anonymous flag will be reset
   571  	if len(schema.Properties) > 0 {
   572  		result.IsNullable = t.isNullable(schema)
   573  		result.IsComplexObject = true
   574  		// no return here, still need to check for additional properties
   575  	}
   576  
   577  	// account for additional properties
   578  	if schema.AdditionalProperties != nil && schema.AdditionalProperties.Schema != nil {
   579  		sch := schema.AdditionalProperties.Schema
   580  		et, er := t.ResolveSchema(sch, sch.Ref.String() == "", false)
   581  		if er != nil {
   582  			err = er
   583  			return
   584  		}
   585  
   586  		result.IsMap = !result.IsComplexObject
   587  
   588  		result.SwaggerType = object
   589  
   590  		if et.IsExternal {
   591  			// external AdditionalProperties are a special case because we look ahead into schemas
   592  			extType, _, _ := t.knownDefGoType(t.ModelName, *sch, t.goTypeName)
   593  			et.GoType = extType
   594  		}
   595  
   596  		// only complex map elements are nullable (when not forced by x-nullable)
   597  		// TODO: figure out if required to check when not discriminated like arrays?
   598  		et.IsNullable = !et.IsArray && t.isNullable(schema.AdditionalProperties.Schema)
   599  		if et.IsNullable {
   600  			result.GoType = "map[string]*" + et.GoType
   601  		} else {
   602  			result.GoType = "map[string]" + et.GoType
   603  		}
   604  
   605  		// Resolving nullability conflicts for:
   606  		// - map[][]...[]{items}
   607  		// - map[]{aliased type}
   608  		//
   609  		// when IsMap is true and the type is a distinct definition,
   610  		// aliased type or anonymous construct generated independently.
   611  		//
   612  		// IsMapNullOverride is to be handled by the generator for special cases
   613  		// where the map element is considered non nullable and the element itself is.
   614  		//
   615  		// This allows to appreciate nullability according to the context
   616  		needsOverride := result.IsMap && (et.IsArray || (sch.Ref.String() != "" || et.IsAliased || et.IsAnonymous))
   617  
   618  		if needsOverride {
   619  			var er error
   620  			if et.IsArray {
   621  				var it resolvedType
   622  				s := sch
   623  				// resolve the last items after nested arrays
   624  				for s.Items != nil && s.Items.Schema != nil {
   625  					it, er = t.ResolveSchema(s.Items.Schema, sch.Ref.String() == "", false)
   626  					if er != nil {
   627  						return
   628  					}
   629  					s = s.Items.Schema
   630  				}
   631  				// mark an override when nullable status conflicts, i.e. when the original type is not already nullable
   632  				if !it.IsAnonymous || it.IsAnonymous && it.IsNullable {
   633  					result.IsMapNullOverride = true
   634  				}
   635  			} else {
   636  				// this locks the generator on the local nullability status
   637  				result.IsMapNullOverride = true
   638  			}
   639  		}
   640  
   641  		t.inferAliasing(&result, schema, isAnonymous, false)
   642  		result.ElemType = &et
   643  		return
   644  	}
   645  
   646  	if len(schema.Properties) > 0 {
   647  		return
   648  	}
   649  
   650  	// an object without property and without AdditionalProperties schema is rendered as interface{}
   651  	result.IsMap = true
   652  	result.SwaggerType = object
   653  	result.IsNullable = false
   654  	// an object without properties but with MinProperties or MaxProperties is rendered as map[string]interface{}
   655  	result.IsInterface = len(schema.Properties) == 0 && !schema.Validations().HasObjectValidations()
   656  	if result.IsInterface {
   657  		result.GoType = iface
   658  	} else {
   659  		result.GoType = "map[string]interface{}"
   660  	}
   661  	return
   662  }
   663  
   664  // nullableBool makes a boolean a pointer when we want to distinguish the zero value from no value set.
   665  // This is the case when:
   666  // - a x-nullable extension says so in the spec
   667  // - it is **not** a read-only property
   668  // - it is a required property
   669  // - it has a default value
   670  func nullableBool(schema *spec.Schema, isRequired bool) bool {
   671  	if nullable := nullableExtension(schema.Extensions); nullable != nil {
   672  		return *nullable
   673  	}
   674  	required := isRequired && schema.Default == nil && !schema.ReadOnly
   675  	optional := !isRequired && (schema.Default != nil || schema.ReadOnly)
   676  
   677  	return required || optional
   678  }
   679  
   680  // nullableNumber makes a number a pointer when we want to distinguish the zero value from no value set.
   681  // This is the case when:
   682  // - a x-nullable extension says so in the spec
   683  // - it is **not** a read-only property
   684  // - it is a required property
   685  // - boundaries defines the zero value as a valid value:
   686  //   - there is a non-exclusive boundary set at the zero value of the type
   687  //   - the [min,max] range crosses the zero value of the type
   688  func nullableNumber(schema *spec.Schema, isRequired bool) bool {
   689  	if nullable := nullableExtension(schema.Extensions); nullable != nil {
   690  		return *nullable
   691  	}
   692  	hasDefault := schema.Default != nil && !swag.IsZero(schema.Default)
   693  
   694  	isMin := schema.Minimum != nil && (*schema.Minimum != 0 || schema.ExclusiveMinimum)
   695  	bcMin := schema.Minimum != nil && *schema.Minimum == 0 && !schema.ExclusiveMinimum
   696  	isMax := schema.Minimum == nil && (schema.Maximum != nil && (*schema.Maximum != 0 || schema.ExclusiveMaximum))
   697  	bcMax := schema.Maximum != nil && *schema.Maximum == 0 && !schema.ExclusiveMaximum
   698  	isMinMax := (schema.Minimum != nil && schema.Maximum != nil && *schema.Minimum < *schema.Maximum)
   699  	bcMinMax := (schema.Minimum != nil && schema.Maximum != nil && (*schema.Minimum < 0 && 0 < *schema.Maximum))
   700  
   701  	nullable := !schema.ReadOnly && (isRequired || (hasDefault && !(isMin || isMax || isMinMax)) || bcMin || bcMax || bcMinMax)
   702  	return nullable
   703  }
   704  
   705  // nullableString makes a string nullable when we want to distinguish the zero value from no value set.
   706  // This is the case when:
   707  // - a x-nullable extension says so in the spec
   708  // - it is **not** a read-only property
   709  // - it is a required property
   710  // - it has a MinLength property set to 0
   711  // - it has a default other than "" (the zero for strings) and no MinLength or zero MinLength
   712  func nullableString(schema *spec.Schema, isRequired bool) bool {
   713  	if nullable := nullableExtension(schema.Extensions); nullable != nil {
   714  		return *nullable
   715  	}
   716  	hasDefault := schema.Default != nil && !swag.IsZero(schema.Default)
   717  
   718  	isMin := schema.MinLength != nil && *schema.MinLength != 0
   719  	bcMin := schema.MinLength != nil && *schema.MinLength == 0
   720  
   721  	nullable := !schema.ReadOnly && (isRequired || (hasDefault && !isMin) || bcMin)
   722  	return nullable
   723  }
   724  
   725  func nullableStrfmt(schema *spec.Schema, isRequired bool) bool {
   726  	notBinary := schema.Format != binary
   727  	if nullable := nullableExtension(schema.Extensions); nullable != nil && notBinary {
   728  		return *nullable
   729  	}
   730  	hasDefault := schema.Default != nil && !swag.IsZero(schema.Default)
   731  
   732  	nullable := !schema.ReadOnly && (isRequired || hasDefault)
   733  	return notBinary && nullable
   734  }
   735  
   736  func nullableExtension(ext spec.Extensions) *bool {
   737  	if ext == nil {
   738  		return nil
   739  	}
   740  
   741  	if boolPtr := boolExtension(ext, xNullable); boolPtr != nil {
   742  		return boolPtr
   743  	}
   744  
   745  	return boolExtension(ext, xIsNullable)
   746  }
   747  
   748  func boolExtension(ext spec.Extensions, key string) *bool {
   749  	if v, ok := ext[key]; ok {
   750  		if bb, ok := v.(bool); ok {
   751  			return &bb
   752  		}
   753  	}
   754  	return nil
   755  }
   756  
   757  func hasEnumCI(ve spec.Extensions) bool {
   758  	v, ok := ve[xGoEnumCI]
   759  	if !ok {
   760  		return false
   761  	}
   762  
   763  	isEnumCI, ok := v.(bool)
   764  	// All enumeration types are case-sensitive by default
   765  	return ok && isEnumCI
   766  }
   767  
   768  func (t *typeResolver) shortCircuitResolveExternal(tpe, pkg, alias string, extType *externalTypeDefinition, schema *spec.Schema, isRequired bool) resolvedType {
   769  	// short circuit type resolution for external types
   770  	debugLogAsJSON("shortCircuitResolveExternal", extType)
   771  
   772  	var result resolvedType
   773  	result.Extensions = schema.Extensions
   774  	result.GoType = tpe
   775  	result.Pkg = pkg
   776  	result.PkgAlias = alias
   777  	result.IsInterface = false
   778  	// by default consider that we have a type with validations. Use hint "interface" or "noValidation" to disable validations
   779  	result.SkipExternalValidation = swag.BoolValue(extType.Hints.NoValidation)
   780  	result.IsNullable = isRequired
   781  
   782  	result.setKind(extType.Hints.Kind)
   783  	if result.IsInterface || result.IsStream {
   784  		result.IsNullable = false
   785  	}
   786  	if extType.Hints.Nullable != nil {
   787  		result.IsNullable = swag.BoolValue(extType.Hints.Nullable)
   788  	}
   789  
   790  	if nullable, ok := t.isNullableOverride(schema); ok {
   791  		result.IsNullable = nullable // x-nullable directive rules them all
   792  	}
   793  
   794  	// other extensions
   795  	if result.IsArray {
   796  		result.IsEmptyOmitted = false
   797  		tpe = "array"
   798  	}
   799  
   800  	result.setExtensions(schema, tpe)
   801  	return result
   802  }
   803  
   804  func (t *typeResolver) ResolveSchema(schema *spec.Schema, isAnonymous, isRequired bool) (result resolvedType, err error) {
   805  	debugLog("resolving schema (anon: %t, req: %t) %s", isAnonymous, isRequired, t.ModelName)
   806  	defer func() {
   807  		debugLog("returning after resolve schema: %s", pretty.Sprint(result))
   808  	}()
   809  
   810  	if schema == nil {
   811  		result.IsInterface = true
   812  		result.GoType = iface
   813  		return
   814  	}
   815  
   816  	extType, isExternalType := t.resolveExternalType(schema.Extensions)
   817  	if isExternalType {
   818  		tpe, pkg, alias := t.knownDefGoType(t.ModelName, *schema, t.goTypeName)
   819  		debugLog("found type %s declared as external, imported from %s as %s. Has type hints? %t, rendered has embedded? %t",
   820  			t.ModelName, pkg, tpe, extType.Hints.Kind != "", extType.Embedded)
   821  
   822  		if extType.Hints.Kind != "" && !extType.Embedded {
   823  			// use hint to qualify type
   824  			debugLog("short circuits external type resolution with hint for %s", tpe)
   825  			result = t.shortCircuitResolveExternal(tpe, pkg, alias, extType, schema, isRequired)
   826  			result.IsExternal = isAnonymous // mark anonymous external types only, not definitions
   827  			return
   828  		}
   829  
   830  		// use spec to qualify type
   831  		debugLog("marking type %s as external embedded: %t", tpe, extType.Embedded)
   832  		defer func() { // enforce bubbling up decisions taken about being an external type
   833  			// mark this type as an embedded external definition if requested
   834  			result.IsEmbedded = extType.Embedded
   835  			result.IsExternal = isAnonymous // for non-embedded, mark anonymous external types only, not definitions
   836  
   837  			result.IsAnonymous = false
   838  			result.IsAliased = true
   839  			result.IsNullable = isRequired
   840  			if extType.Hints.Nullable != nil {
   841  				result.IsNullable = swag.BoolValue(extType.Hints.Nullable)
   842  			}
   843  
   844  			result.IsMap = false
   845  			result.AliasedType = result.GoType
   846  			result.IsInterface = false
   847  
   848  			if result.IsEmbedded {
   849  				result.ElemType = &resolvedType{
   850  					IsExternal:             isAnonymous, // mark anonymous external types only, not definitions
   851  					IsInterface:            false,
   852  					Pkg:                    extType.Import.Package,
   853  					PkgAlias:               extType.Import.Alias,
   854  					SkipExternalValidation: swag.BoolValue(extType.Hints.NoValidation),
   855  				}
   856  				if extType.Import.Alias != "" {
   857  					result.ElemType.GoType = extType.Import.Alias + "." + extType.Type
   858  				} else {
   859  					result.ElemType.GoType = extType.Type
   860  				}
   861  				result.ElemType.setKind(extType.Hints.Kind)
   862  				if result.IsInterface || result.IsStream {
   863  					result.ElemType.IsNullable = false
   864  				}
   865  				if extType.Hints.Nullable != nil {
   866  					result.ElemType.IsNullable = swag.BoolValue(extType.Hints.Nullable)
   867  				}
   868  				// embedded external: by default consider validation is skipped for the external type
   869  				//
   870  				// NOTE: at this moment the template generates a type assertion, so this setting does not really matter
   871  				// for embedded types.
   872  				if extType.Hints.NoValidation != nil {
   873  					result.ElemType.SkipExternalValidation = swag.BoolValue(extType.Hints.NoValidation)
   874  				} else {
   875  					result.ElemType.SkipExternalValidation = true
   876  				}
   877  			} else {
   878  				// non-embedded external type: by default consider that validation is enabled (SkipExternalValidation: false)
   879  				result.SkipExternalValidation = swag.BoolValue(extType.Hints.NoValidation)
   880  			}
   881  
   882  			if nullable, ok := t.isNullableOverride(schema); ok {
   883  				result.IsNullable = nullable
   884  			}
   885  		}()
   886  	}
   887  
   888  	tpe := t.firstType(schema)
   889  	var returns bool
   890  
   891  	guardValidations(tpe, schema, schema.Type...)
   892  
   893  	returns, result, err = t.resolveSchemaRef(schema, isRequired)
   894  
   895  	if returns {
   896  		if !isAnonymous {
   897  			result.IsMap = false
   898  			result.IsComplexObject = true
   899  		}
   900  
   901  		return
   902  	}
   903  
   904  	defer func() {
   905  		result.setExtensions(schema, tpe)
   906  	}()
   907  
   908  	// special case of swagger type "file", rendered as io.ReadCloser interface
   909  	if t.firstType(schema) == file {
   910  		result.SwaggerType = file
   911  		result.IsPrimitive = true
   912  		result.IsNullable = false
   913  		result.GoType = formatMapping[str][binary]
   914  		result.IsStream = true
   915  		return
   916  	}
   917  
   918  	returns, result, err = t.resolveFormat(schema, isAnonymous, isRequired)
   919  	if returns {
   920  		return
   921  	}
   922  
   923  	result.IsNullable = t.isNullable(schema) || isRequired
   924  
   925  	switch tpe {
   926  	case array:
   927  		result, err = t.resolveArray(schema, isAnonymous, false)
   928  
   929  	case file, number, integer, boolean:
   930  		result.Extensions = schema.Extensions
   931  		result.GoType = typeMapping[tpe]
   932  		result.SwaggerType = tpe
   933  		t.inferAliasing(&result, schema, isAnonymous, isRequired)
   934  
   935  		switch tpe {
   936  		case boolean:
   937  			result.IsPrimitive = true
   938  			result.IsCustomFormatter = false
   939  			result.IsNullable = nullableBool(schema, isRequired)
   940  		case number, integer:
   941  			result.IsPrimitive = true
   942  			result.IsCustomFormatter = false
   943  			result.IsNullable = nullableNumber(schema, isRequired)
   944  		case file:
   945  		}
   946  
   947  	case str:
   948  		result.GoType = str
   949  		result.SwaggerType = str
   950  		t.inferAliasing(&result, schema, isAnonymous, isRequired)
   951  
   952  		result.IsPrimitive = true
   953  		result.IsNullable = nullableString(schema, isRequired)
   954  		result.Extensions = schema.Extensions
   955  
   956  	case object:
   957  		result, err = t.resolveObject(schema, isAnonymous)
   958  		if err != nil {
   959  			result = resolvedType{}
   960  			break
   961  		}
   962  		result.HasDiscriminator = schema.Discriminator != ""
   963  
   964  	case "null":
   965  		if schema.Validations().HasObjectValidations() {
   966  			// no explicit object type, but inferred from object validations:
   967  			// this makes the type a map[string]interface{} instead of interface{}
   968  			result, err = t.resolveObject(schema, isAnonymous)
   969  			if err != nil {
   970  				result = resolvedType{}
   971  				break
   972  			}
   973  			result.HasDiscriminator = schema.Discriminator != ""
   974  			break
   975  		}
   976  
   977  		result.GoType = iface
   978  		result.SwaggerType = object
   979  		result.IsNullable = false
   980  		result.IsInterface = true
   981  
   982  	default:
   983  		err = fmt.Errorf("unresolvable: %v (format %q)", schema.Type, schema.Format)
   984  	}
   985  
   986  	return
   987  }
   988  
   989  func warnSkipValidation(types interface{}) func(string, interface{}) {
   990  	return func(validation string, value interface{}) {
   991  		value = reflect.Indirect(reflect.ValueOf(value)).Interface()
   992  		log.Printf("warning: validation %s (value: %v) not compatible with type %v. Skipped", validation, value, types)
   993  	}
   994  }
   995  
   996  // guardValidations removes (with a warning) validations that don't fit with the schema type.
   997  //
   998  // Notice that the "enum" validation is allowed on any type but file.
   999  func guardValidations(tpe string, schema interface {
  1000  	Validations() spec.SchemaValidations
  1001  	SetValidations(spec.SchemaValidations)
  1002  }, types ...string) {
  1003  
  1004  	v := schema.Validations()
  1005  	if len(types) == 0 {
  1006  		types = []string{tpe}
  1007  	}
  1008  	defer func() {
  1009  		schema.SetValidations(v)
  1010  	}()
  1011  
  1012  	if tpe != array {
  1013  		v.ClearArrayValidations(warnSkipValidation(types))
  1014  	}
  1015  
  1016  	if tpe != str && tpe != file {
  1017  		v.ClearStringValidations(warnSkipValidation(types))
  1018  	}
  1019  
  1020  	if tpe != object {
  1021  		v.ClearObjectValidations(warnSkipValidation(types))
  1022  	}
  1023  
  1024  	if tpe != number && tpe != integer {
  1025  		v.ClearNumberValidations(warnSkipValidation(types))
  1026  	}
  1027  
  1028  	if tpe == file {
  1029  		// keep MinLength/MaxLength on file
  1030  		if v.Pattern != "" {
  1031  			warnSkipValidation(types)("pattern", v.Pattern)
  1032  			v.Pattern = ""
  1033  		}
  1034  		if v.HasEnum() {
  1035  			warnSkipValidation(types)("enum", v.Enum)
  1036  			v.Enum = nil
  1037  		}
  1038  	}
  1039  
  1040  	// other cases:  mapped as interface{}: no validations allowed but Enum
  1041  }
  1042  
  1043  // guardFormatConflicts handles all conflicting properties
  1044  // (for schema model or simple schema) when a format is set.
  1045  //
  1046  // At this moment, validation guards already handle all known conflicts, but for the
  1047  // special case of binary (i.e. io.Reader).
  1048  func guardFormatConflicts(format string, schema interface {
  1049  	Validations() spec.SchemaValidations
  1050  	SetValidations(spec.SchemaValidations)
  1051  }) {
  1052  	v := schema.Validations()
  1053  	msg := fmt.Sprintf("for format %q", format)
  1054  
  1055  	// for this format, no additional validations are supported
  1056  	if format == "binary" {
  1057  		// no validations supported on binary fields at this moment (io.Reader)
  1058  		v.ClearStringValidations(warnSkipValidation(msg))
  1059  		if v.HasEnum() {
  1060  			warnSkipValidation(msg)
  1061  			v.Enum = nil
  1062  		}
  1063  		schema.SetValidations(v)
  1064  	}
  1065  	// more cases should be inserted here if they arise
  1066  }
  1067  
  1068  // resolvedType is a swagger type that has been resolved and analyzed for usage
  1069  // in a template
  1070  type resolvedType struct {
  1071  	IsAnonymous       bool
  1072  	IsArray           bool
  1073  	IsMap             bool
  1074  	IsInterface       bool
  1075  	IsPrimitive       bool
  1076  	IsCustomFormatter bool
  1077  	IsAliased         bool
  1078  	IsNullable        bool
  1079  	IsStream          bool
  1080  	IsEmptyOmitted    bool
  1081  	IsJSONString      bool
  1082  	IsEnumCI          bool
  1083  	IsBase64          bool
  1084  	IsExternal        bool
  1085  
  1086  	// A tuple gets rendered as an anonymous struct with P{index} as property name
  1087  	IsTuple            bool
  1088  	HasAdditionalItems bool
  1089  
  1090  	// A complex object gets rendered as a struct
  1091  	IsComplexObject bool
  1092  
  1093  	// A polymorphic type
  1094  	IsBaseType       bool
  1095  	HasDiscriminator bool
  1096  
  1097  	GoType        string
  1098  	Pkg           string
  1099  	PkgAlias      string
  1100  	AliasedType   string
  1101  	SwaggerType   string
  1102  	SwaggerFormat string
  1103  	Extensions    spec.Extensions
  1104  
  1105  	// The type of the element in a slice or map
  1106  	ElemType *resolvedType
  1107  
  1108  	// IsMapNullOverride indicates that a nullable object is used within an
  1109  	// aliased map. In this case, the reference is not rendered with a pointer
  1110  	IsMapNullOverride bool
  1111  
  1112  	// IsSuperAlias indicates that the aliased type is really the same type,
  1113  	// e.g. in golang, this translates to: type A = B
  1114  	IsSuperAlias bool
  1115  
  1116  	// IsEmbedded applies to externally defined types. When embedded, a type
  1117  	// is generated in models that embeds the external type, with the Validate
  1118  	// method.
  1119  	IsEmbedded bool
  1120  
  1121  	SkipExternalValidation bool
  1122  }
  1123  
  1124  // Zero returns an initializer for the type
  1125  func (rt resolvedType) Zero() string {
  1126  	// if type is aliased, provide zero from the aliased type
  1127  	if rt.IsAliased {
  1128  		if zr, ok := zeroes[rt.AliasedType]; ok {
  1129  			return rt.GoType + "(" + zr + ")"
  1130  		}
  1131  	}
  1132  	// zero function provided as native or by strfmt function
  1133  	if zr, ok := zeroes[rt.GoType]; ok {
  1134  		return zr
  1135  	}
  1136  	// map and slice initializer
  1137  	if rt.IsMap {
  1138  		return "make(" + rt.GoType + ", 50)"
  1139  	} else if rt.IsArray {
  1140  		return "make(" + rt.GoType + ", 0, 50)"
  1141  	}
  1142  	// object initializer
  1143  	if rt.IsTuple || rt.IsComplexObject {
  1144  		if rt.IsNullable {
  1145  			return "new(" + rt.GoType + ")"
  1146  		}
  1147  		return rt.GoType + "{}"
  1148  	}
  1149  	// interface initializer
  1150  	if rt.IsInterface {
  1151  		return "nil"
  1152  	}
  1153  
  1154  	return ""
  1155  }
  1156  
  1157  // ToString returns a string conversion for a type akin to a string
  1158  func (rt resolvedType) ToString(value string) string {
  1159  	if !rt.IsPrimitive || rt.SwaggerType != "string" || rt.IsStream {
  1160  		return ""
  1161  	}
  1162  	if rt.IsCustomFormatter {
  1163  		if rt.IsAliased {
  1164  			return fmt.Sprintf("%s(%s).String()", rt.AliasedType, value)
  1165  		}
  1166  		return fmt.Sprintf("%s.String()", value)
  1167  	}
  1168  	var deref string
  1169  	if rt.IsNullable {
  1170  		deref = "*"
  1171  	}
  1172  	if rt.GoType == "string" || rt.GoType == "*string" {
  1173  		return fmt.Sprintf("%s%s", deref, value)
  1174  	}
  1175  
  1176  	return fmt.Sprintf("string(%s%s)", deref, value)
  1177  }
  1178  
  1179  func (rt *resolvedType) setExtensions(schema *spec.Schema, origType string) {
  1180  	rt.IsEnumCI = hasEnumCI(schema.Extensions)
  1181  	rt.setIsEmptyOmitted(schema, origType)
  1182  	rt.setIsJSONString(schema, origType)
  1183  
  1184  	if customTag, found := schema.Extensions[xGoCustomTag]; found {
  1185  		if rt.Extensions == nil {
  1186  			rt.Extensions = make(spec.Extensions)
  1187  		}
  1188  		rt.Extensions[xGoCustomTag] = customTag
  1189  	}
  1190  }
  1191  
  1192  func (rt *resolvedType) setIsEmptyOmitted(schema *spec.Schema, tpe string) {
  1193  	if v, found := schema.Extensions[xOmitEmpty]; found {
  1194  		omitted, cast := v.(bool)
  1195  		rt.IsEmptyOmitted = omitted && cast
  1196  		return
  1197  	}
  1198  	// array of primitives are by default not empty-omitted, but arrays of aliased type are
  1199  	rt.IsEmptyOmitted = (tpe != array) || (tpe == array && rt.IsAliased)
  1200  }
  1201  
  1202  func (rt *resolvedType) setIsJSONString(schema *spec.Schema, tpe string) {
  1203  	_, found := schema.Extensions[xGoJSONString]
  1204  	if !found {
  1205  		rt.IsJSONString = false
  1206  		return
  1207  	}
  1208  	rt.IsJSONString = true
  1209  }
  1210  
  1211  func (rt *resolvedType) setKind(kind string) {
  1212  	if kind != "" {
  1213  		debugLog("overriding kind for %s as %s", rt.GoType, kind)
  1214  	}
  1215  	switch kind {
  1216  	case "map":
  1217  		rt.IsMap = true
  1218  		rt.IsArray = false
  1219  		rt.IsComplexObject = false
  1220  		rt.IsInterface = false
  1221  		rt.IsStream = false
  1222  		rt.IsTuple = false
  1223  		rt.IsPrimitive = false
  1224  		rt.SwaggerType = object
  1225  	case "array":
  1226  		rt.IsMap = false
  1227  		rt.IsArray = true
  1228  		rt.IsComplexObject = false
  1229  		rt.IsInterface = false
  1230  		rt.IsStream = false
  1231  		rt.IsTuple = false
  1232  		rt.IsPrimitive = false
  1233  		rt.SwaggerType = array
  1234  	case "object":
  1235  		rt.IsMap = false
  1236  		rt.IsArray = false
  1237  		rt.IsComplexObject = true
  1238  		rt.IsInterface = false
  1239  		rt.IsStream = false
  1240  		rt.IsTuple = false
  1241  		rt.IsPrimitive = false
  1242  		rt.SwaggerType = object
  1243  	case "interface", "null":
  1244  		rt.IsMap = false
  1245  		rt.IsArray = false
  1246  		rt.IsComplexObject = false
  1247  		rt.IsInterface = true
  1248  		rt.IsStream = false
  1249  		rt.IsTuple = false
  1250  		rt.IsPrimitive = false
  1251  		rt.SwaggerType = iface
  1252  	case "stream":
  1253  		rt.IsMap = false
  1254  		rt.IsArray = false
  1255  		rt.IsComplexObject = false
  1256  		rt.IsInterface = false
  1257  		rt.IsStream = true
  1258  		rt.IsTuple = false
  1259  		rt.IsPrimitive = false
  1260  		rt.SwaggerType = file
  1261  	case "tuple":
  1262  		rt.IsMap = false
  1263  		rt.IsArray = false
  1264  		rt.IsComplexObject = false
  1265  		rt.IsInterface = false
  1266  		rt.IsStream = false
  1267  		rt.IsTuple = true
  1268  		rt.IsPrimitive = false
  1269  		rt.SwaggerType = array
  1270  	case "primitive":
  1271  		rt.IsMap = false
  1272  		rt.IsArray = false
  1273  		rt.IsComplexObject = false
  1274  		rt.IsInterface = false
  1275  		rt.IsStream = false
  1276  		rt.IsTuple = false
  1277  		rt.IsPrimitive = true
  1278  	case "":
  1279  		break
  1280  	default:
  1281  		log.Printf("warning: unsupported hint value for external type: %q. Skipped", kind)
  1282  	}
  1283  }