github.com/go-swagger/go-swagger@v0.31.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, _ 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  //
   175  //	 type: mytype
   176  //	 import:
   177  //	   package:
   178  //	   alias:
   179  //	 hints:
   180  //	   kind: map|object|array|interface|primitive|stream|tuple
   181  //	   nullable: true|false
   182  //	embedded: true
   183  type externalTypeDefinition struct {
   184  	Type   string
   185  	Import struct {
   186  		Package string
   187  		Alias   string
   188  	}
   189  	Hints struct {
   190  		Kind         string
   191  		Nullable     *bool
   192  		NoValidation *bool
   193  	}
   194  	Embedded bool
   195  }
   196  
   197  func hasExternalType(ext spec.Extensions) (*externalTypeDefinition, bool) {
   198  	v, ok := ext[xGoType]
   199  	if !ok {
   200  		return nil, false
   201  	}
   202  
   203  	var extType externalTypeDefinition
   204  	err := mapstructure.Decode(v, &extType)
   205  	if err != nil {
   206  		log.Printf("warning: x-go-type extension could not be decoded (%v). Skipped", v)
   207  		return nil, false
   208  	}
   209  
   210  	return &extType, true
   211  }
   212  
   213  func (t typeResolver) resolveExternalType(ext spec.Extensions) (*externalTypeDefinition, bool) {
   214  	extType, hasExt := hasExternalType(ext)
   215  	if !hasExt {
   216  		return nil, false
   217  	}
   218  
   219  	// NOTE:
   220  	// * basic deconfliction of the default alias
   221  	// * if no package is specified, defaults to models (as provided from CLI or defaut generation location for models)
   222  	toAlias := func(pkg string) string {
   223  		mangled := GoLangOpts().ManglePackageName(pkg, "")
   224  		return deconflictPkg(mangled, func(in string) string {
   225  			return in + "ext"
   226  		})
   227  	}
   228  
   229  	switch {
   230  	case extType.Import.Package != "" && extType.Import.Alias == "":
   231  		extType.Import.Alias = toAlias(extType.Import.Package)
   232  	case extType.Import.Package == "" && extType.Import.Alias != "":
   233  		extType.Import.Package = t.ModelsFullPkg
   234  	case extType.Import.Package == "" && extType.Import.Alias == "":
   235  		// in this case, the external type is assumed to be present in the current package.
   236  		// For completion, whenever this type is used in anonymous types declared by operations,
   237  		// we assume this is the package where models are expected to be found.
   238  		extType.Import.Package = t.ModelsFullPkg
   239  		if extType.Import.Package != "" {
   240  			extType.Import.Alias = toAlias(extType.Import.Package)
   241  		}
   242  	}
   243  
   244  	debugLogAsJSON("known def external %s type", xGoType, extType)
   245  
   246  	return extType, true
   247  }
   248  
   249  type typeResolver struct {
   250  	Doc           *loads.Document
   251  	ModelsPackage string // package alias (e.g. "models")
   252  	ModelsFullPkg string // fully qualified package (e.g. "github.com/example/models")
   253  	ModelName     string
   254  	KnownDefs     map[string]struct{}
   255  	// unexported fields
   256  	keepDefinitionsPkg string
   257  	knownDefsKept      map[string]struct{}
   258  	definitionPkg      string // pkg alias to fill in GenSchema.Pkg
   259  }
   260  
   261  // NewWithModelName clones a type resolver and specifies a new model name
   262  func (t *typeResolver) NewWithModelName(name string) *typeResolver {
   263  	tt := newTypeResolver(t.ModelsPackage, t.ModelsFullPkg, t.Doc)
   264  	tt.ModelName = name
   265  
   266  	// propagates kept definitions
   267  	tt.keepDefinitionsPkg = t.keepDefinitionsPkg
   268  	tt.knownDefsKept = t.knownDefsKept
   269  	tt.definitionPkg = t.definitionPkg
   270  	return tt
   271  }
   272  
   273  // withKeepDefinitionsPackage instructs the type resolver to keep previously resolved package name for
   274  // definitions known at the moment it is first called.
   275  func (t *typeResolver) withKeepDefinitionsPackage(definitionsPackage string) *typeResolver {
   276  	t.keepDefinitionsPkg = definitionsPackage
   277  	t.knownDefsKept = make(map[string]struct{}, len(t.KnownDefs))
   278  	for k := range t.KnownDefs {
   279  		t.knownDefsKept[k] = struct{}{}
   280  	}
   281  	return t
   282  }
   283  
   284  // withDefinitionPackage sets the definition pkg that object/struct types to be generated
   285  // in GenSchema.Pkg field.
   286  // ModelsPackage field can not replace definitionPkg since ModelsPackage will be prepend to .GoType,
   287  // while definitionPkg is just used to fill the .Pkg in GenSchema
   288  func (t *typeResolver) withDefinitionPackage(pkg string) *typeResolver {
   289  	t.definitionPkg = pkg
   290  	return t
   291  }
   292  
   293  func (t *typeResolver) resolveSchemaRef(schema *spec.Schema, isRequired bool) (returns bool, result resolvedType, err error) {
   294  	if schema.Ref.String() == "" {
   295  		return
   296  	}
   297  	debugLog("resolving ref (anon: %t, req: %t) %s", false, isRequired, schema.Ref.String())
   298  
   299  	returns = true
   300  	var ref *spec.Schema
   301  	var er error
   302  
   303  	ref, er = spec.ResolveRef(t.Doc.Spec(), &schema.Ref)
   304  	if er != nil {
   305  		debugLog("error resolving ref %s: %v", schema.Ref.String(), er)
   306  		err = er
   307  		return
   308  	}
   309  
   310  	extType, isExternalType := t.resolveExternalType(schema.Extensions)
   311  	if isExternalType {
   312  		// deal with validations for an aliased external type
   313  		result.SkipExternalValidation = swag.BoolValue(extType.Hints.NoValidation)
   314  	}
   315  
   316  	res, er := t.ResolveSchema(ref, false, isRequired)
   317  	if er != nil {
   318  		err = er
   319  		return
   320  	}
   321  	result = res
   322  
   323  	tn := filepath.Base(schema.Ref.GetURL().Fragment)
   324  	tpe, pkg, alias := t.knownDefGoType(tn, *ref, t.goTypeName)
   325  	debugLog("type name %s, package %s, alias %s", tpe, pkg, alias)
   326  	if tpe != "" {
   327  		result.GoType = tpe
   328  		result.Pkg = pkg
   329  		result.PkgAlias = alias
   330  	}
   331  	result.HasDiscriminator = res.HasDiscriminator
   332  	result.IsBaseType = result.HasDiscriminator
   333  	result.IsNullable = result.IsNullable || t.isNullable(ref) // this has to be overridden for slices and maps
   334  	result.IsEnumCI = false
   335  	return
   336  }
   337  
   338  func (t *typeResolver) inferAliasing(result *resolvedType, _ *spec.Schema, isAnonymous bool, _ bool) {
   339  	if !isAnonymous && t.ModelName != "" {
   340  		result.AliasedType = result.GoType
   341  		result.IsAliased = true
   342  		result.GoType = t.goTypeName(t.ModelName)
   343  		result.Pkg = t.definitionPkg
   344  	}
   345  }
   346  
   347  func (t *typeResolver) resolveFormat(schema *spec.Schema, isAnonymous bool, isRequired bool) (returns bool, result resolvedType, err error) {
   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  	if nullable, ok := t.isNullableOverride(schema); ok {
   404  		return nullable
   405  	}
   406  
   407  	return len(schema.Properties) > 0 || len(schema.AllOf) > 0
   408  }
   409  
   410  // isNullableOverride determines a nullable flag forced by an extension
   411  func (t *typeResolver) isNullableOverride(schema *spec.Schema) (bool, bool) {
   412  	check := func(extension string) (bool, bool) {
   413  		v, found := schema.Extensions[extension]
   414  		nullable, cast := v.(bool)
   415  		return nullable, found && cast
   416  	}
   417  
   418  	if nullable, ok := check(xIsNullable); ok {
   419  		return nullable, ok
   420  	}
   421  
   422  	if nullable, ok := check(xNullable); ok {
   423  		return nullable, ok
   424  	}
   425  
   426  	return false, false
   427  }
   428  
   429  func (t *typeResolver) firstType(schema *spec.Schema) string {
   430  	if len(schema.Type) == 0 || schema.Type[0] == "" {
   431  		return object
   432  	}
   433  	if len(schema.Type) > 1 {
   434  		// JSON-Schema multiple types, e.g. {"type": [ "object", "array" ]} are not supported.
   435  		// TODO: should keep the first _supported_ type, e.g. skip null
   436  		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])
   437  	}
   438  	return schema.Type[0]
   439  }
   440  
   441  func (t *typeResolver) resolveArray(schema *spec.Schema, isAnonymous, isRequired bool) (result resolvedType, err error) {
   442  	debugLog("resolving array (anon: %t, req: %t)", isAnonymous, isRequired)
   443  
   444  	result.IsArray = true
   445  	result.IsNullable = false
   446  
   447  	if schema.AdditionalItems != nil {
   448  		result.HasAdditionalItems = (schema.AdditionalItems.Allows || schema.AdditionalItems.Schema != nil)
   449  	}
   450  
   451  	if schema.Items == nil {
   452  		result.GoType = "[]" + iface
   453  		result.SwaggerType = array
   454  		result.SwaggerFormat = ""
   455  		t.inferAliasing(&result, schema, isAnonymous, isRequired)
   456  
   457  		return
   458  	}
   459  
   460  	if len(schema.Items.Schemas) > 0 {
   461  		result.IsArray = false
   462  		result.IsTuple = true
   463  		result.SwaggerType = array
   464  		result.SwaggerFormat = ""
   465  		t.inferAliasing(&result, schema, isAnonymous, isRequired)
   466  
   467  		return
   468  	}
   469  
   470  	rt, er := t.ResolveSchema(schema.Items.Schema, true, false)
   471  	if er != nil {
   472  		err = er
   473  		return
   474  	}
   475  
   476  	// Override the general nullability rule from ResolveSchema() in array elements:
   477  	// - only complex items are nullable (when not discriminated, not forced by x-nullable)
   478  	// - arrays of allOf have non nullable elements when not forced by x-nullable
   479  	elem := schema.Items.Schema
   480  	if elem.Ref.String() != "" {
   481  		// drill into $ref to figure out whether we want the element type to nullable or not
   482  		resolved, erf := spec.ResolveRef(t.Doc.Spec(), &elem.Ref)
   483  		if erf != nil {
   484  			debugLog("error resolving ref %s: %v", schema.Ref.String(), erf)
   485  		}
   486  		elem = resolved
   487  	}
   488  
   489  	debugLogAsJSON("resolved item for %s", rt.GoType, elem)
   490  	if nullable, ok := t.isNullableOverride(elem); ok {
   491  		debugLog("found nullable override in element %s: %t", rt.GoType, nullable)
   492  		rt.IsNullable = nullable
   493  	} else {
   494  		// this differs from isNullable for elements with AllOf
   495  		debugLog("no nullable override in element %s: Properties: %t, HasDiscriminator: %t", rt.GoType, len(elem.Properties) > 0, rt.HasDiscriminator)
   496  		rt.IsNullable = len(elem.Properties) > 0 && !rt.HasDiscriminator
   497  	}
   498  
   499  	result.GoType = "[]" + rt.GoType
   500  	if rt.IsNullable && !strings.HasPrefix(rt.GoType, "*") {
   501  		result.GoType = "[]*" + rt.GoType
   502  	}
   503  
   504  	result.ElemType = &rt
   505  	result.SwaggerType = array
   506  	result.SwaggerFormat = ""
   507  	result.IsEnumCI = hasEnumCI(schema.Extensions)
   508  	t.inferAliasing(&result, schema, isAnonymous, isRequired)
   509  	result.Extensions = schema.Extensions
   510  
   511  	return
   512  }
   513  
   514  func (t *typeResolver) goTypeName(nm string) string {
   515  	if len(t.knownDefsKept) > 0 {
   516  		// if a definitions package has been defined, already resolved definitions are
   517  		// always resolved against their original package (e.g. "models"), and not the
   518  		// current package.
   519  		// This allows complex anonymous extra schemas to reuse known definitions generated in another package.
   520  		if _, ok := t.knownDefsKept[nm]; ok {
   521  			return strings.Join([]string{t.keepDefinitionsPkg, swag.ToGoName(nm)}, ".")
   522  		}
   523  	}
   524  
   525  	if t.ModelsPackage == "" {
   526  		return swag.ToGoName(nm)
   527  	}
   528  	if _, ok := t.KnownDefs[nm]; ok {
   529  		return strings.Join([]string{t.ModelsPackage, swag.ToGoName(nm)}, ".")
   530  	}
   531  	return swag.ToGoName(nm)
   532  }
   533  
   534  func (t *typeResolver) resolveObject(schema *spec.Schema, isAnonymous bool) (result resolvedType, err error) {
   535  	debugLog("resolving object %s (anon: %t, req: %t)", t.ModelName, isAnonymous, false)
   536  
   537  	result.IsAnonymous = isAnonymous
   538  
   539  	result.IsBaseType = schema.Discriminator != ""
   540  	if !isAnonymous {
   541  		result.SwaggerType = object
   542  		tpe, pkg, alias := t.knownDefGoType(t.ModelName, *schema, t.goTypeName)
   543  		result.GoType = tpe
   544  		result.Pkg = pkg
   545  		result.PkgAlias = alias
   546  	}
   547  	if len(schema.AllOf) > 0 {
   548  		result.GoType = t.goTypeName(t.ModelName)
   549  		result.IsComplexObject = true
   550  		var isNullable bool
   551  		for _, sch := range schema.AllOf {
   552  			p := sch
   553  			if t.isNullable(&p) {
   554  				isNullable = true
   555  			}
   556  		}
   557  		if override, ok := t.isNullableOverride(schema); ok {
   558  			// prioritize x-nullable extensions
   559  			result.IsNullable = override
   560  		} else {
   561  			result.IsNullable = isNullable
   562  		}
   563  		result.SwaggerType = object
   564  		return
   565  	}
   566  
   567  	// if this schema has properties, build a map of property name to
   568  	// resolved type, this should also flag the object as anonymous,
   569  	// when a ref is found, the anonymous flag will be reset
   570  	if len(schema.Properties) > 0 {
   571  		result.IsNullable = t.isNullable(schema)
   572  		result.IsComplexObject = true
   573  		// no return here, still need to check for additional properties
   574  	}
   575  
   576  	// account for additional properties
   577  	if schema.AdditionalProperties != nil && schema.AdditionalProperties.Schema != nil {
   578  		sch := schema.AdditionalProperties.Schema
   579  		et, er := t.ResolveSchema(sch, sch.Ref.String() == "", false)
   580  		if er != nil {
   581  			err = er
   582  			return
   583  		}
   584  
   585  		result.IsMap = !result.IsComplexObject
   586  
   587  		result.SwaggerType = object
   588  
   589  		if et.IsExternal {
   590  			// external AdditionalProperties are a special case because we look ahead into schemas
   591  			extType, _, _ := t.knownDefGoType(t.ModelName, *sch, t.goTypeName)
   592  			et.GoType = extType
   593  		}
   594  
   595  		// only complex map elements are nullable (when not forced by x-nullable)
   596  		// TODO: figure out if required to check when not discriminated like arrays?
   597  		et.IsNullable = !et.IsArray && t.isNullable(schema.AdditionalProperties.Schema)
   598  		if et.IsNullable {
   599  			result.GoType = "map[string]*" + et.GoType
   600  		} else {
   601  			result.GoType = "map[string]" + et.GoType
   602  		}
   603  
   604  		// Resolving nullability conflicts for:
   605  		// - map[][]...[]{items}
   606  		// - map[]{aliased type}
   607  		//
   608  		// when IsMap is true and the type is a distinct definition,
   609  		// aliased type or anonymous construct generated independently.
   610  		//
   611  		// IsMapNullOverride is to be handled by the generator for special cases
   612  		// where the map element is considered non nullable and the element itself is.
   613  		//
   614  		// This allows to appreciate nullability according to the context
   615  		needsOverride := result.IsMap && (et.IsArray || (sch.Ref.String() != "" || et.IsAliased || et.IsAnonymous))
   616  
   617  		if needsOverride {
   618  			var er error
   619  			if et.IsArray {
   620  				var it resolvedType
   621  				s := sch
   622  				// resolve the last items after nested arrays
   623  				for s.Items != nil && s.Items.Schema != nil {
   624  					it, er = t.ResolveSchema(s.Items.Schema, sch.Ref.String() == "", false)
   625  					if er != nil {
   626  						return
   627  					}
   628  					s = s.Items.Schema
   629  				}
   630  				// mark an override when nullable status conflicts, i.e. when the original type is not already nullable
   631  				if !it.IsAnonymous || it.IsAnonymous && it.IsNullable {
   632  					result.IsMapNullOverride = true
   633  				}
   634  			} else {
   635  				// this locks the generator on the local nullability status
   636  				result.IsMapNullOverride = true
   637  			}
   638  		}
   639  
   640  		t.inferAliasing(&result, schema, isAnonymous, false)
   641  		result.ElemType = &et
   642  		return
   643  	}
   644  
   645  	if len(schema.Properties) > 0 {
   646  		return
   647  	}
   648  
   649  	// an object without property and without AdditionalProperties schema is rendered as interface{}
   650  	result.IsMap = true
   651  	result.SwaggerType = object
   652  	result.IsNullable = false
   653  	// an object without properties but with MinProperties or MaxProperties is rendered as map[string]interface{}
   654  	result.IsInterface = len(schema.Properties) == 0 && !schema.Validations().HasObjectValidations()
   655  	if result.IsInterface {
   656  		result.GoType = iface
   657  	} else {
   658  		result.GoType = "map[string]interface{}"
   659  	}
   660  	return
   661  }
   662  
   663  // nullableBool makes a boolean a pointer when we want to distinguish the zero value from no value set.
   664  // This is the case when:
   665  // - a x-nullable extension says so in the spec
   666  // - it is **not** a read-only property
   667  // - it is a required property
   668  // - it has a default value
   669  func nullableBool(schema *spec.Schema, isRequired bool) bool {
   670  	if nullable := nullableExtension(schema.Extensions); nullable != nil {
   671  		return *nullable
   672  	}
   673  	required := isRequired && schema.Default == nil && !schema.ReadOnly
   674  	optional := !isRequired && (schema.Default != nil || schema.ReadOnly)
   675  
   676  	return required || optional
   677  }
   678  
   679  // nullableNumber makes a number a pointer when we want to distinguish the zero value from no value set.
   680  // This is the case when:
   681  // - a x-nullable extension says so in the spec
   682  // - it is **not** a read-only property
   683  // - it is a required property
   684  // - boundaries defines the zero value as a valid value:
   685  //   - there is a non-exclusive boundary set at the zero value of the type
   686  //   - the [min,max] range crosses the zero value of the type
   687  func nullableNumber(schema *spec.Schema, isRequired bool) bool {
   688  	if nullable := nullableExtension(schema.Extensions); nullable != nil {
   689  		return *nullable
   690  	}
   691  	hasDefault := schema.Default != nil && !swag.IsZero(schema.Default)
   692  
   693  	isMin := schema.Minimum != nil && (*schema.Minimum != 0 || schema.ExclusiveMinimum)
   694  	bcMin := schema.Minimum != nil && *schema.Minimum == 0 && !schema.ExclusiveMinimum
   695  	isMax := schema.Minimum == nil && (schema.Maximum != nil && (*schema.Maximum != 0 || schema.ExclusiveMaximum))
   696  	bcMax := schema.Maximum != nil && *schema.Maximum == 0 && !schema.ExclusiveMaximum
   697  	isMinMax := (schema.Minimum != nil && schema.Maximum != nil && *schema.Minimum < *schema.Maximum)
   698  	bcMinMax := (schema.Minimum != nil && schema.Maximum != nil && (*schema.Minimum < 0 && 0 < *schema.Maximum))
   699  
   700  	nullable := !schema.ReadOnly && (isRequired || (hasDefault && !(isMin || isMax || isMinMax)) || bcMin || bcMax || bcMinMax)
   701  	return nullable
   702  }
   703  
   704  // nullableString makes a string nullable when we want to distinguish the zero value from no value set.
   705  // This is the case when:
   706  // - a x-nullable extension says so in the spec
   707  // - it is **not** a read-only property
   708  // - it is a required property
   709  // - it has a MinLength property set to 0
   710  // - it has a default other than "" (the zero for strings) and no MinLength or zero MinLength
   711  func nullableString(schema *spec.Schema, isRequired bool) bool {
   712  	if nullable := nullableExtension(schema.Extensions); nullable != nil {
   713  		return *nullable
   714  	}
   715  	hasDefault := schema.Default != nil && !swag.IsZero(schema.Default)
   716  
   717  	isMin := schema.MinLength != nil && *schema.MinLength != 0
   718  	bcMin := schema.MinLength != nil && *schema.MinLength == 0
   719  
   720  	nullable := !schema.ReadOnly && (isRequired || (hasDefault && !isMin) || bcMin)
   721  	return nullable
   722  }
   723  
   724  func nullableStrfmt(schema *spec.Schema, isRequired bool) bool {
   725  	notBinary := schema.Format != binary
   726  	if nullable := nullableExtension(schema.Extensions); nullable != nil && notBinary {
   727  		return *nullable
   728  	}
   729  	hasDefault := schema.Default != nil && !swag.IsZero(schema.Default)
   730  
   731  	nullable := !schema.ReadOnly && (isRequired || hasDefault)
   732  	return notBinary && nullable
   733  }
   734  
   735  func nullableExtension(ext spec.Extensions) *bool {
   736  	if ext == nil {
   737  		return nil
   738  	}
   739  
   740  	if boolPtr := boolExtension(ext, xNullable); boolPtr != nil {
   741  		return boolPtr
   742  	}
   743  
   744  	return boolExtension(ext, xIsNullable)
   745  }
   746  
   747  func boolExtension(ext spec.Extensions, key string) *bool {
   748  	if v, ok := ext[key]; ok {
   749  		if bb, ok := v.(bool); ok {
   750  			return &bb
   751  		}
   752  	}
   753  	return nil
   754  }
   755  
   756  func hasEnumCI(ve spec.Extensions) bool {
   757  	v, ok := ve[xGoEnumCI]
   758  	if !ok {
   759  		return false
   760  	}
   761  
   762  	isEnumCI, ok := v.(bool)
   763  	// All enumeration types are case-sensitive by default
   764  	return ok && isEnumCI
   765  }
   766  
   767  func (t *typeResolver) shortCircuitResolveExternal(tpe, pkg, alias string, extType *externalTypeDefinition, schema *spec.Schema, isRequired bool) resolvedType {
   768  	// short circuit type resolution for external types
   769  	debugLogAsJSON("shortCircuitResolveExternal", extType)
   770  
   771  	var result resolvedType
   772  	result.Extensions = schema.Extensions
   773  	result.GoType = tpe
   774  	result.Pkg = pkg
   775  	result.PkgAlias = alias
   776  	result.IsInterface = false
   777  	// by default consider that we have a type with validations. Use hint "interface" or "noValidation" to disable validations
   778  	result.SkipExternalValidation = swag.BoolValue(extType.Hints.NoValidation)
   779  	result.IsNullable = isRequired
   780  
   781  	result.setKind(extType.Hints.Kind)
   782  	if result.IsInterface || result.IsStream {
   783  		result.IsNullable = false
   784  	}
   785  	if extType.Hints.Nullable != nil {
   786  		result.IsNullable = swag.BoolValue(extType.Hints.Nullable)
   787  	}
   788  
   789  	if nullable, ok := t.isNullableOverride(schema); ok {
   790  		result.IsNullable = nullable // x-nullable directive rules them all
   791  	}
   792  
   793  	// other extensions
   794  	if result.IsArray {
   795  		result.IsEmptyOmitted = false
   796  		tpe = "array"
   797  	}
   798  
   799  	result.setExtensions(schema, tpe)
   800  	return result
   801  }
   802  
   803  func (t *typeResolver) ResolveSchema(schema *spec.Schema, isAnonymous, isRequired bool) (result resolvedType, err error) {
   804  	debugLog("resolving schema (anon: %t, req: %t) %s", isAnonymous, isRequired, t.ModelName)
   805  	defer func() {
   806  		debugLog("returning after resolve schema: %s", pretty.Sprint(result))
   807  	}()
   808  
   809  	if schema == nil {
   810  		result.IsInterface = true
   811  		result.GoType = iface
   812  		return
   813  	}
   814  
   815  	extType, isExternalType := t.resolveExternalType(schema.Extensions)
   816  	if isExternalType {
   817  		tpe, pkg, alias := t.knownDefGoType(t.ModelName, *schema, t.goTypeName)
   818  		debugLog("found type %s declared as external, imported from %s as %s. Has type hints? %t, rendered has embedded? %t",
   819  			t.ModelName, pkg, tpe, extType.Hints.Kind != "", extType.Embedded)
   820  
   821  		if extType.Hints.Kind != "" && !extType.Embedded {
   822  			// use hint to qualify type
   823  			debugLog("short circuits external type resolution with hint for %s", tpe)
   824  			result = t.shortCircuitResolveExternal(tpe, pkg, alias, extType, schema, isRequired)
   825  			result.IsExternal = isAnonymous // mark anonymous external types only, not definitions
   826  			return
   827  		}
   828  
   829  		// use spec to qualify type
   830  		debugLog("marking type %s as external embedded: %t", tpe, extType.Embedded)
   831  		defer func() { // enforce bubbling up decisions taken about being an external type
   832  			// mark this type as an embedded external definition if requested
   833  			result.IsEmbedded = extType.Embedded
   834  			result.IsExternal = isAnonymous // for non-embedded, mark anonymous external types only, not definitions
   835  
   836  			result.IsAnonymous = false
   837  			result.IsAliased = true
   838  			result.IsNullable = isRequired
   839  			if extType.Hints.Nullable != nil {
   840  				result.IsNullable = swag.BoolValue(extType.Hints.Nullable)
   841  			}
   842  
   843  			result.IsMap = false
   844  			result.AliasedType = result.GoType
   845  			result.IsInterface = false
   846  
   847  			if result.IsEmbedded {
   848  				result.ElemType = &resolvedType{
   849  					IsExternal:             isAnonymous, // mark anonymous external types only, not definitions
   850  					IsInterface:            false,
   851  					Pkg:                    extType.Import.Package,
   852  					PkgAlias:               extType.Import.Alias,
   853  					SkipExternalValidation: swag.BoolValue(extType.Hints.NoValidation),
   854  				}
   855  				if extType.Import.Alias != "" {
   856  					result.ElemType.GoType = extType.Import.Alias + "." + extType.Type
   857  				} else {
   858  					result.ElemType.GoType = extType.Type
   859  				}
   860  				result.ElemType.setKind(extType.Hints.Kind)
   861  				if result.IsInterface || result.IsStream {
   862  					result.ElemType.IsNullable = false
   863  				}
   864  				if extType.Hints.Nullable != nil {
   865  					result.ElemType.IsNullable = swag.BoolValue(extType.Hints.Nullable)
   866  				}
   867  				// embedded external: by default consider validation is skipped for the external type
   868  				//
   869  				// NOTE: at this moment the template generates a type assertion, so this setting does not really matter
   870  				// for embedded types.
   871  				if extType.Hints.NoValidation != nil {
   872  					result.ElemType.SkipExternalValidation = swag.BoolValue(extType.Hints.NoValidation)
   873  				} else {
   874  					result.ElemType.SkipExternalValidation = true
   875  				}
   876  			} else {
   877  				// non-embedded external type: by default consider that validation is enabled (SkipExternalValidation: false)
   878  				result.SkipExternalValidation = swag.BoolValue(extType.Hints.NoValidation)
   879  			}
   880  
   881  			if nullable, ok := t.isNullableOverride(schema); ok {
   882  				result.IsNullable = nullable
   883  			}
   884  		}()
   885  	}
   886  
   887  	tpe := t.firstType(schema)
   888  	var returns bool
   889  
   890  	guardValidations(tpe, schema, schema.Type...)
   891  
   892  	returns, result, err = t.resolveSchemaRef(schema, isRequired)
   893  
   894  	if returns {
   895  		if !isAnonymous {
   896  			result.IsMap = false
   897  			result.IsComplexObject = true
   898  		}
   899  
   900  		return
   901  	}
   902  
   903  	defer func() {
   904  		result.setExtensions(schema, tpe)
   905  	}()
   906  
   907  	// special case of swagger type "file", rendered as io.ReadCloser interface
   908  	if t.firstType(schema) == file {
   909  		result.SwaggerType = file
   910  		result.IsPrimitive = true
   911  		result.IsNullable = false
   912  		result.GoType = formatMapping[str][binary]
   913  		result.IsStream = true
   914  		return
   915  	}
   916  
   917  	returns, result, err = t.resolveFormat(schema, isAnonymous, isRequired)
   918  	if returns {
   919  		return
   920  	}
   921  
   922  	result.IsNullable = t.isNullable(schema) || isRequired
   923  
   924  	switch tpe {
   925  	case array:
   926  		result, err = t.resolveArray(schema, isAnonymous, false)
   927  
   928  	case file, number, integer, boolean:
   929  		result.Extensions = schema.Extensions
   930  		result.GoType = typeMapping[tpe]
   931  		result.SwaggerType = tpe
   932  		t.inferAliasing(&result, schema, isAnonymous, isRequired)
   933  
   934  		switch tpe {
   935  		case boolean:
   936  			result.IsPrimitive = true
   937  			result.IsCustomFormatter = false
   938  			result.IsNullable = nullableBool(schema, isRequired)
   939  		case number, integer:
   940  			result.IsPrimitive = true
   941  			result.IsCustomFormatter = false
   942  			result.IsNullable = nullableNumber(schema, isRequired)
   943  		case file:
   944  		}
   945  
   946  	case str:
   947  		result.GoType = str
   948  		result.SwaggerType = str
   949  		t.inferAliasing(&result, schema, isAnonymous, isRequired)
   950  
   951  		result.IsPrimitive = true
   952  		result.IsNullable = nullableString(schema, isRequired)
   953  		result.Extensions = schema.Extensions
   954  
   955  	case object:
   956  		result, err = t.resolveObject(schema, isAnonymous)
   957  		if err != nil {
   958  			result = resolvedType{}
   959  			break
   960  		}
   961  		result.HasDiscriminator = schema.Discriminator != ""
   962  
   963  	case "null":
   964  		if schema.Validations().HasObjectValidations() {
   965  			// no explicit object type, but inferred from object validations:
   966  			// this makes the type a map[string]interface{} instead of interface{}
   967  			result, err = t.resolveObject(schema, isAnonymous)
   968  			if err != nil {
   969  				result = resolvedType{}
   970  				break
   971  			}
   972  			result.HasDiscriminator = schema.Discriminator != ""
   973  			break
   974  		}
   975  
   976  		result.GoType = iface
   977  		result.SwaggerType = object
   978  		result.IsNullable = false
   979  		result.IsInterface = true
   980  
   981  	default:
   982  		err = fmt.Errorf("unresolvable: %v (format %q)", schema.Type, schema.Format)
   983  	}
   984  
   985  	return
   986  }
   987  
   988  func warnSkipValidation(types interface{}) func(string, interface{}) {
   989  	return func(validation string, value interface{}) {
   990  		value = reflect.Indirect(reflect.ValueOf(value)).Interface()
   991  		log.Printf("warning: validation %s (value: %v) not compatible with type %v. Skipped", validation, value, types)
   992  	}
   993  }
   994  
   995  // guardValidations removes (with a warning) validations that don't fit with the schema type.
   996  //
   997  // Notice that the "enum" validation is allowed on any type but file.
   998  func guardValidations(tpe string, schema interface {
   999  	Validations() spec.SchemaValidations
  1000  	SetValidations(spec.SchemaValidations)
  1001  }, types ...string,
  1002  ) {
  1003  	v := schema.Validations()
  1004  	if len(types) == 0 {
  1005  		types = []string{tpe}
  1006  	}
  1007  	defer func() {
  1008  		schema.SetValidations(v)
  1009  	}()
  1010  
  1011  	if tpe != array {
  1012  		v.ClearArrayValidations(warnSkipValidation(types))
  1013  	}
  1014  
  1015  	if tpe != str && tpe != file {
  1016  		v.ClearStringValidations(warnSkipValidation(types))
  1017  	}
  1018  
  1019  	if tpe != object {
  1020  		v.ClearObjectValidations(warnSkipValidation(types))
  1021  	}
  1022  
  1023  	if tpe != number && tpe != integer {
  1024  		v.ClearNumberValidations(warnSkipValidation(types))
  1025  	}
  1026  
  1027  	if tpe == file {
  1028  		// keep MinLength/MaxLength on file
  1029  		if v.Pattern != "" {
  1030  			warnSkipValidation(types)("pattern", v.Pattern)
  1031  			v.Pattern = ""
  1032  		}
  1033  		if v.HasEnum() {
  1034  			warnSkipValidation(types)("enum", v.Enum)
  1035  			v.Enum = nil
  1036  		}
  1037  	}
  1038  
  1039  	// other cases:  mapped as interface{}: no validations allowed but Enum
  1040  }
  1041  
  1042  // guardFormatConflicts handles all conflicting properties
  1043  // (for schema model or simple schema) when a format is set.
  1044  //
  1045  // At this moment, validation guards already handle all known conflicts, but for the
  1046  // special case of binary (i.e. io.Reader).
  1047  func guardFormatConflicts(format string, schema interface {
  1048  	Validations() spec.SchemaValidations
  1049  	SetValidations(spec.SchemaValidations)
  1050  },
  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, _ 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  }