github.com/josephspurrier/go-swagger@v0.2.1-0.20221129144919-1f672a142a00/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  //
   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, schema *spec.Schema, isAnonymous bool, isRequired 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  
   349  	if schema.Format != "" {
   350  		// defaults to string
   351  		result.SwaggerType = str
   352  		if len(schema.Type) > 0 {
   353  			result.SwaggerType = schema.Type[0]
   354  		}
   355  
   356  		debugLog("resolving format (anon: %t, req: %t)", isAnonymous, isRequired)
   357  		schFmt := strings.ReplaceAll(schema.Format, "-", "")
   358  		if fmm, ok := formatMapping[result.SwaggerType]; ok {
   359  			if tpe, ok := fmm[schFmt]; ok {
   360  				returns = true
   361  				result.GoType = tpe
   362  				_, result.IsCustomFormatter = customFormatters[tpe]
   363  			}
   364  		}
   365  		if tpe, ok := typeMapping[schFmt]; !returns && ok {
   366  			returns = true
   367  			result.GoType = tpe
   368  			_, result.IsCustomFormatter = customFormatters[tpe]
   369  		}
   370  
   371  		result.SwaggerFormat = schema.Format
   372  		t.inferAliasing(&result, schema, isAnonymous, isRequired)
   373  		// special case of swagger format "binary", rendered as io.ReadCloser interface and is therefore not a primitive type
   374  		// TODO: should set IsCustomFormatter=false in this case.
   375  		result.IsPrimitive = schFmt != binary
   376  		result.IsStream = schFmt == binary
   377  		result.IsBase64 = schFmt == b64
   378  		// propagate extensions in resolvedType
   379  		result.Extensions = schema.Extensions
   380  
   381  		switch result.SwaggerType {
   382  		case str:
   383  			result.IsNullable = nullableStrfmt(schema, isRequired)
   384  		case number, integer:
   385  			result.IsNullable = nullableNumber(schema, isRequired)
   386  		default:
   387  			result.IsNullable = t.isNullable(schema)
   388  		}
   389  	}
   390  
   391  	guardFormatConflicts(schema.Format, schema)
   392  	return
   393  }
   394  
   395  // isNullable hints the generator as to render the type with a pointer or not.
   396  //
   397  // A schema is deemed nullable (i.e. rendered by a pointer) when:
   398  // - a custom extension says it has to be so
   399  // - it is an object with properties
   400  // - it is a composed object (allOf)
   401  //
   402  // The interpretation of Required as a mean to make a type nullable is carried out elsewhere.
   403  func (t *typeResolver) isNullable(schema *spec.Schema) bool {
   404  
   405  	if nullable, ok := t.isNullableOverride(schema); ok {
   406  		return nullable
   407  	}
   408  
   409  	return len(schema.Properties) > 0 || len(schema.AllOf) > 0
   410  }
   411  
   412  // isNullableOverride determines a nullable flag forced by an extension
   413  func (t *typeResolver) isNullableOverride(schema *spec.Schema) (bool, bool) {
   414  	check := func(extension string) (bool, bool) {
   415  		v, found := schema.Extensions[extension]
   416  		nullable, cast := v.(bool)
   417  		return nullable, found && cast
   418  	}
   419  
   420  	if nullable, ok := check(xIsNullable); ok {
   421  		return nullable, ok
   422  	}
   423  
   424  	if nullable, ok := check(xNullable); ok {
   425  		return nullable, ok
   426  	}
   427  
   428  	return false, false
   429  }
   430  
   431  func (t *typeResolver) firstType(schema *spec.Schema) string {
   432  	if len(schema.Type) == 0 || schema.Type[0] == "" {
   433  		return object
   434  	}
   435  	if len(schema.Type) > 1 {
   436  		// JSON-Schema multiple types, e.g. {"type": [ "object", "array" ]} are not supported.
   437  		// TODO: should keep the first _supported_ type, e.g. skip null
   438  		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])
   439  	}
   440  	return schema.Type[0]
   441  }
   442  
   443  func (t *typeResolver) resolveArray(schema *spec.Schema, isAnonymous, isRequired bool) (result resolvedType, err error) {
   444  	debugLog("resolving array (anon: %t, req: %t)", isAnonymous, isRequired)
   445  
   446  	result.IsArray = true
   447  	result.IsNullable = false
   448  
   449  	if schema.AdditionalItems != nil {
   450  		result.HasAdditionalItems = (schema.AdditionalItems.Allows || schema.AdditionalItems.Schema != nil)
   451  	}
   452  
   453  	if schema.Items == nil {
   454  		result.GoType = "[]" + iface
   455  		result.SwaggerType = array
   456  		result.SwaggerFormat = ""
   457  		t.inferAliasing(&result, schema, isAnonymous, isRequired)
   458  
   459  		return
   460  	}
   461  
   462  	if len(schema.Items.Schemas) > 0 {
   463  		result.IsArray = false
   464  		result.IsTuple = true
   465  		result.SwaggerType = array
   466  		result.SwaggerFormat = ""
   467  		t.inferAliasing(&result, schema, isAnonymous, isRequired)
   468  
   469  		return
   470  	}
   471  
   472  	rt, er := t.ResolveSchema(schema.Items.Schema, true, false)
   473  	if er != nil {
   474  		err = er
   475  		return
   476  	}
   477  
   478  	// Override the general nullability rule from ResolveSchema() in array elements:
   479  	// - only complex items are nullable (when not discriminated, not forced by x-nullable)
   480  	// - arrays of allOf have non nullable elements when not forced by x-nullable
   481  	elem := schema.Items.Schema
   482  	if elem.Ref.String() != "" {
   483  		// drill into $ref to figure out whether we want the element type to nullable or not
   484  		resolved, erf := spec.ResolveRef(t.Doc.Spec(), &elem.Ref)
   485  		if erf != nil {
   486  			debugLog("error resolving ref %s: %v", schema.Ref.String(), erf)
   487  		}
   488  		elem = resolved
   489  	}
   490  
   491  	debugLogAsJSON("resolved item for %s", rt.GoType, elem)
   492  	if nullable, ok := t.isNullableOverride(elem); ok {
   493  		debugLog("found nullable override in element %s: %t", rt.GoType, nullable)
   494  		rt.IsNullable = nullable
   495  	} else {
   496  		// this differs from isNullable for elements with AllOf
   497  		debugLog("no nullable override in element %s: Properties: %t, HasDiscriminator: %t", rt.GoType, len(elem.Properties) > 0, rt.HasDiscriminator)
   498  		rt.IsNullable = len(elem.Properties) > 0 && !rt.HasDiscriminator
   499  	}
   500  
   501  	result.GoType = "[]" + rt.GoType
   502  	if rt.IsNullable && !strings.HasPrefix(rt.GoType, "*") {
   503  		result.GoType = "[]*" + rt.GoType
   504  	}
   505  
   506  	result.ElemType = &rt
   507  	result.SwaggerType = array
   508  	result.SwaggerFormat = ""
   509  	result.IsEnumCI = hasEnumCI(schema.Extensions)
   510  	t.inferAliasing(&result, schema, isAnonymous, isRequired)
   511  	result.Extensions = schema.Extensions
   512  
   513  	return
   514  }
   515  
   516  func (t *typeResolver) goTypeName(nm string) string {
   517  	if len(t.knownDefsKept) > 0 {
   518  		// if a definitions package has been defined, already resolved definitions are
   519  		// always resolved against their original package (e.g. "models"), and not the
   520  		// current package.
   521  		// This allows complex anonymous extra schemas to reuse known definitions generated in another package.
   522  		if _, ok := t.knownDefsKept[nm]; ok {
   523  			return strings.Join([]string{t.keepDefinitionsPkg, swag.ToGoName(nm)}, ".")
   524  		}
   525  	}
   526  
   527  	if t.ModelsPackage == "" {
   528  		return swag.ToGoName(nm)
   529  	}
   530  	if _, ok := t.KnownDefs[nm]; ok {
   531  		return strings.Join([]string{t.ModelsPackage, swag.ToGoName(nm)}, ".")
   532  	}
   533  	return swag.ToGoName(nm)
   534  }
   535  
   536  func (t *typeResolver) resolveObject(schema *spec.Schema, isAnonymous bool) (result resolvedType, err error) {
   537  	debugLog("resolving object %s (anon: %t, req: %t)", t.ModelName, isAnonymous, false)
   538  
   539  	result.IsAnonymous = isAnonymous
   540  
   541  	result.IsBaseType = schema.Discriminator != ""
   542  	if !isAnonymous {
   543  		result.SwaggerType = object
   544  		tpe, pkg, alias := t.knownDefGoType(t.ModelName, *schema, t.goTypeName)
   545  		result.GoType = tpe
   546  		result.Pkg = pkg
   547  		result.PkgAlias = alias
   548  	}
   549  	if len(schema.AllOf) > 0 {
   550  		result.GoType = t.goTypeName(t.ModelName)
   551  		result.IsComplexObject = true
   552  		var isNullable bool
   553  		for _, sch := range schema.AllOf {
   554  			p := sch
   555  			if t.isNullable(&p) {
   556  				isNullable = true
   557  			}
   558  		}
   559  		if override, ok := t.isNullableOverride(schema); ok {
   560  			// prioritize x-nullable extensions
   561  			result.IsNullable = override
   562  		} else {
   563  			result.IsNullable = isNullable
   564  		}
   565  		result.SwaggerType = object
   566  		return
   567  	}
   568  
   569  	// if this schema has properties, build a map of property name to
   570  	// resolved type, this should also flag the object as anonymous,
   571  	// when a ref is found, the anonymous flag will be reset
   572  	if len(schema.Properties) > 0 {
   573  		result.IsNullable = t.isNullable(schema)
   574  		result.IsComplexObject = true
   575  		// no return here, still need to check for additional properties
   576  	}
   577  
   578  	// account for additional properties
   579  	if schema.AdditionalProperties != nil && schema.AdditionalProperties.Schema != nil {
   580  		sch := schema.AdditionalProperties.Schema
   581  		et, er := t.ResolveSchema(sch, sch.Ref.String() == "", false)
   582  		if er != nil {
   583  			err = er
   584  			return
   585  		}
   586  
   587  		result.IsMap = !result.IsComplexObject
   588  
   589  		result.SwaggerType = object
   590  
   591  		if et.IsExternal {
   592  			// external AdditionalProperties are a special case because we look ahead into schemas
   593  			extType, _, _ := t.knownDefGoType(t.ModelName, *sch, t.goTypeName)
   594  			et.GoType = extType
   595  		}
   596  
   597  		// only complex map elements are nullable (when not forced by x-nullable)
   598  		// TODO: figure out if required to check when not discriminated like arrays?
   599  		et.IsNullable = !et.IsArray && t.isNullable(schema.AdditionalProperties.Schema)
   600  		if et.IsNullable {
   601  			result.GoType = "map[string]*" + et.GoType
   602  		} else {
   603  			result.GoType = "map[string]" + et.GoType
   604  		}
   605  
   606  		// Resolving nullability conflicts for:
   607  		// - map[][]...[]{items}
   608  		// - map[]{aliased type}
   609  		//
   610  		// when IsMap is true and the type is a distinct definition,
   611  		// aliased type or anonymous construct generated independently.
   612  		//
   613  		// IsMapNullOverride is to be handled by the generator for special cases
   614  		// where the map element is considered non nullable and the element itself is.
   615  		//
   616  		// This allows to appreciate nullability according to the context
   617  		needsOverride := result.IsMap && (et.IsArray || (sch.Ref.String() != "" || et.IsAliased || et.IsAnonymous))
   618  
   619  		if needsOverride {
   620  			var er error
   621  			if et.IsArray {
   622  				var it resolvedType
   623  				s := sch
   624  				// resolve the last items after nested arrays
   625  				for s.Items != nil && s.Items.Schema != nil {
   626  					it, er = t.ResolveSchema(s.Items.Schema, sch.Ref.String() == "", false)
   627  					if er != nil {
   628  						return
   629  					}
   630  					s = s.Items.Schema
   631  				}
   632  				// mark an override when nullable status conflicts, i.e. when the original type is not already nullable
   633  				if !it.IsAnonymous || it.IsAnonymous && it.IsNullable {
   634  					result.IsMapNullOverride = true
   635  				}
   636  			} else {
   637  				// this locks the generator on the local nullability status
   638  				result.IsMapNullOverride = true
   639  			}
   640  		}
   641  
   642  		t.inferAliasing(&result, schema, isAnonymous, false)
   643  		result.ElemType = &et
   644  		return
   645  	}
   646  
   647  	if len(schema.Properties) > 0 {
   648  		return
   649  	}
   650  
   651  	// an object without property and without AdditionalProperties schema is rendered as interface{}
   652  	result.IsMap = true
   653  	result.SwaggerType = object
   654  	result.IsNullable = false
   655  	// an object without properties but with MinProperties or MaxProperties is rendered as map[string]interface{}
   656  	result.IsInterface = len(schema.Properties) == 0 && !schema.Validations().HasObjectValidations()
   657  	if result.IsInterface {
   658  		result.GoType = iface
   659  	} else {
   660  		result.GoType = "map[string]interface{}"
   661  	}
   662  	return
   663  }
   664  
   665  // nullableBool makes a boolean a pointer when we want to distinguish the zero value from no value set.
   666  // This is the case when:
   667  // - a x-nullable extension says so in the spec
   668  // - it is **not** a read-only property
   669  // - it is a required property
   670  // - it has a default value
   671  func nullableBool(schema *spec.Schema, isRequired bool) bool {
   672  	if nullable := nullableExtension(schema.Extensions); nullable != nil {
   673  		return *nullable
   674  	}
   675  	required := isRequired && schema.Default == nil && !schema.ReadOnly
   676  	optional := !isRequired && (schema.Default != nil || schema.ReadOnly)
   677  
   678  	return required || optional
   679  }
   680  
   681  // nullableNumber makes a number a pointer when we want to distinguish the zero value from no value set.
   682  // This is the case when:
   683  // - a x-nullable extension says so in the spec
   684  // - it is **not** a read-only property
   685  // - it is a required property
   686  // - boundaries defines the zero value as a valid value:
   687  //   - there is a non-exclusive boundary set at the zero value of the type
   688  //   - the [min,max] range crosses the zero value of the type
   689  func nullableNumber(schema *spec.Schema, isRequired bool) bool {
   690  	if nullable := nullableExtension(schema.Extensions); nullable != nil {
   691  		return *nullable
   692  	}
   693  	hasDefault := schema.Default != nil && !swag.IsZero(schema.Default)
   694  
   695  	isMin := schema.Minimum != nil && (*schema.Minimum != 0 || schema.ExclusiveMinimum)
   696  	bcMin := schema.Minimum != nil && *schema.Minimum == 0 && !schema.ExclusiveMinimum
   697  	isMax := schema.Minimum == nil && (schema.Maximum != nil && (*schema.Maximum != 0 || schema.ExclusiveMaximum))
   698  	bcMax := schema.Maximum != nil && *schema.Maximum == 0 && !schema.ExclusiveMaximum
   699  	isMinMax := (schema.Minimum != nil && schema.Maximum != nil && *schema.Minimum < *schema.Maximum)
   700  	bcMinMax := (schema.Minimum != nil && schema.Maximum != nil && (*schema.Minimum < 0 && 0 < *schema.Maximum))
   701  
   702  	nullable := !schema.ReadOnly && (isRequired || (hasDefault && !(isMin || isMax || isMinMax)) || bcMin || bcMax || bcMinMax)
   703  	return nullable
   704  }
   705  
   706  // nullableString makes a string nullable when we want to distinguish the zero value from no value set.
   707  // This is the case when:
   708  // - a x-nullable extension says so in the spec
   709  // - it is **not** a read-only property
   710  // - it is a required property
   711  // - it has a MinLength property set to 0
   712  // - it has a default other than "" (the zero for strings) and no MinLength or zero MinLength
   713  func nullableString(schema *spec.Schema, isRequired bool) bool {
   714  	if nullable := nullableExtension(schema.Extensions); nullable != nil {
   715  		return *nullable
   716  	}
   717  	hasDefault := schema.Default != nil && !swag.IsZero(schema.Default)
   718  
   719  	isMin := schema.MinLength != nil && *schema.MinLength != 0
   720  	bcMin := schema.MinLength != nil && *schema.MinLength == 0
   721  
   722  	nullable := !schema.ReadOnly && (isRequired || (hasDefault && !isMin) || bcMin)
   723  	return nullable
   724  }
   725  
   726  func nullableStrfmt(schema *spec.Schema, isRequired bool) bool {
   727  	notBinary := schema.Format != binary
   728  	if nullable := nullableExtension(schema.Extensions); nullable != nil && notBinary {
   729  		return *nullable
   730  	}
   731  	hasDefault := schema.Default != nil && !swag.IsZero(schema.Default)
   732  
   733  	nullable := !schema.ReadOnly && (isRequired || hasDefault)
   734  	return notBinary && nullable
   735  }
   736  
   737  func nullableExtension(ext spec.Extensions) *bool {
   738  	if ext == nil {
   739  		return nil
   740  	}
   741  
   742  	if boolPtr := boolExtension(ext, xNullable); boolPtr != nil {
   743  		return boolPtr
   744  	}
   745  
   746  	return boolExtension(ext, xIsNullable)
   747  }
   748  
   749  func boolExtension(ext spec.Extensions, key string) *bool {
   750  	if v, ok := ext[key]; ok {
   751  		if bb, ok := v.(bool); ok {
   752  			return &bb
   753  		}
   754  	}
   755  	return nil
   756  }
   757  
   758  func hasEnumCI(ve spec.Extensions) bool {
   759  	v, ok := ve[xGoEnumCI]
   760  	if !ok {
   761  		return false
   762  	}
   763  
   764  	isEnumCI, ok := v.(bool)
   765  	// All enumeration types are case-sensitive by default
   766  	return ok && isEnumCI
   767  }
   768  
   769  func (t *typeResolver) shortCircuitResolveExternal(tpe, pkg, alias string, extType *externalTypeDefinition, schema *spec.Schema, isRequired bool) resolvedType {
   770  	// short circuit type resolution for external types
   771  	debugLogAsJSON("shortCircuitResolveExternal", extType)
   772  
   773  	var result resolvedType
   774  	result.Extensions = schema.Extensions
   775  	result.GoType = tpe
   776  	result.Pkg = pkg
   777  	result.PkgAlias = alias
   778  	result.IsInterface = false
   779  	// by default consider that we have a type with validations. Use hint "interface" or "noValidation" to disable validations
   780  	result.SkipExternalValidation = swag.BoolValue(extType.Hints.NoValidation)
   781  	result.IsNullable = isRequired
   782  
   783  	result.setKind(extType.Hints.Kind)
   784  	if result.IsInterface || result.IsStream {
   785  		result.IsNullable = false
   786  	}
   787  	if extType.Hints.Nullable != nil {
   788  		result.IsNullable = swag.BoolValue(extType.Hints.Nullable)
   789  	}
   790  
   791  	if nullable, ok := t.isNullableOverride(schema); ok {
   792  		result.IsNullable = nullable // x-nullable directive rules them all
   793  	}
   794  
   795  	// other extensions
   796  	if result.IsArray {
   797  		result.IsEmptyOmitted = false
   798  		tpe = "array"
   799  	}
   800  
   801  	result.setExtensions(schema, tpe)
   802  	return result
   803  }
   804  
   805  func (t *typeResolver) ResolveSchema(schema *spec.Schema, isAnonymous, isRequired bool) (result resolvedType, err error) {
   806  	debugLog("resolving schema (anon: %t, req: %t) %s", isAnonymous, isRequired, t.ModelName)
   807  	defer func() {
   808  		debugLog("returning after resolve schema: %s", pretty.Sprint(result))
   809  	}()
   810  
   811  	if schema == nil {
   812  		result.IsInterface = true
   813  		result.GoType = iface
   814  		return
   815  	}
   816  
   817  	extType, isExternalType := t.resolveExternalType(schema.Extensions)
   818  	if isExternalType {
   819  		tpe, pkg, alias := t.knownDefGoType(t.ModelName, *schema, t.goTypeName)
   820  		debugLog("found type %s declared as external, imported from %s as %s. Has type hints? %t, rendered has embedded? %t",
   821  			t.ModelName, pkg, tpe, extType.Hints.Kind != "", extType.Embedded)
   822  
   823  		if extType.Hints.Kind != "" && !extType.Embedded {
   824  			// use hint to qualify type
   825  			debugLog("short circuits external type resolution with hint for %s", tpe)
   826  			result = t.shortCircuitResolveExternal(tpe, pkg, alias, extType, schema, isRequired)
   827  			result.IsExternal = isAnonymous // mark anonymous external types only, not definitions
   828  			return
   829  		}
   830  
   831  		// use spec to qualify type
   832  		debugLog("marking type %s as external embedded: %t", tpe, extType.Embedded)
   833  		defer func() { // enforce bubbling up decisions taken about being an external type
   834  			// mark this type as an embedded external definition if requested
   835  			result.IsEmbedded = extType.Embedded
   836  			result.IsExternal = isAnonymous // for non-embedded, mark anonymous external types only, not definitions
   837  
   838  			result.IsAnonymous = false
   839  			result.IsAliased = true
   840  			result.IsNullable = isRequired
   841  			if extType.Hints.Nullable != nil {
   842  				result.IsNullable = swag.BoolValue(extType.Hints.Nullable)
   843  			}
   844  
   845  			result.IsMap = false
   846  			result.AliasedType = result.GoType
   847  			result.IsInterface = false
   848  
   849  			if result.IsEmbedded {
   850  				result.ElemType = &resolvedType{
   851  					IsExternal:             isAnonymous, // mark anonymous external types only, not definitions
   852  					IsInterface:            false,
   853  					Pkg:                    extType.Import.Package,
   854  					PkgAlias:               extType.Import.Alias,
   855  					SkipExternalValidation: swag.BoolValue(extType.Hints.NoValidation),
   856  				}
   857  				if extType.Import.Alias != "" {
   858  					result.ElemType.GoType = extType.Import.Alias + "." + extType.Type
   859  				} else {
   860  					result.ElemType.GoType = extType.Type
   861  				}
   862  				result.ElemType.setKind(extType.Hints.Kind)
   863  				if result.IsInterface || result.IsStream {
   864  					result.ElemType.IsNullable = false
   865  				}
   866  				if extType.Hints.Nullable != nil {
   867  					result.ElemType.IsNullable = swag.BoolValue(extType.Hints.Nullable)
   868  				}
   869  				// embedded external: by default consider validation is skipped for the external type
   870  				//
   871  				// NOTE: at this moment the template generates a type assertion, so this setting does not really matter
   872  				// for embedded types.
   873  				if extType.Hints.NoValidation != nil {
   874  					result.ElemType.SkipExternalValidation = swag.BoolValue(extType.Hints.NoValidation)
   875  				} else {
   876  					result.ElemType.SkipExternalValidation = true
   877  				}
   878  			} else {
   879  				// non-embedded external type: by default consider that validation is enabled (SkipExternalValidation: false)
   880  				result.SkipExternalValidation = swag.BoolValue(extType.Hints.NoValidation)
   881  			}
   882  
   883  			if nullable, ok := t.isNullableOverride(schema); ok {
   884  				result.IsNullable = nullable
   885  			}
   886  		}()
   887  	}
   888  
   889  	tpe := t.firstType(schema)
   890  	var returns bool
   891  
   892  	guardValidations(tpe, schema, schema.Type...)
   893  
   894  	returns, result, err = t.resolveSchemaRef(schema, isRequired)
   895  
   896  	if returns {
   897  		if !isAnonymous {
   898  			result.IsMap = false
   899  			result.IsComplexObject = true
   900  		}
   901  
   902  		return
   903  	}
   904  
   905  	defer func() {
   906  		result.setExtensions(schema, tpe)
   907  	}()
   908  
   909  	// special case of swagger type "file", rendered as io.ReadCloser interface
   910  	if t.firstType(schema) == file {
   911  		result.SwaggerType = file
   912  		result.IsPrimitive = true
   913  		result.IsNullable = false
   914  		result.GoType = formatMapping[str][binary]
   915  		result.IsStream = true
   916  		return
   917  	}
   918  
   919  	returns, result, err = t.resolveFormat(schema, isAnonymous, isRequired)
   920  	if returns {
   921  		return
   922  	}
   923  
   924  	result.IsNullable = t.isNullable(schema) || isRequired
   925  
   926  	switch tpe {
   927  	case array:
   928  		result, err = t.resolveArray(schema, isAnonymous, false)
   929  
   930  	case file, number, integer, boolean:
   931  		result.Extensions = schema.Extensions
   932  		result.GoType = typeMapping[tpe]
   933  		result.SwaggerType = tpe
   934  		t.inferAliasing(&result, schema, isAnonymous, isRequired)
   935  
   936  		switch tpe {
   937  		case boolean:
   938  			result.IsPrimitive = true
   939  			result.IsCustomFormatter = false
   940  			result.IsNullable = nullableBool(schema, isRequired)
   941  		case number, integer:
   942  			result.IsPrimitive = true
   943  			result.IsCustomFormatter = false
   944  			result.IsNullable = nullableNumber(schema, isRequired)
   945  		case file:
   946  		}
   947  
   948  	case str:
   949  		result.GoType = str
   950  		result.SwaggerType = str
   951  		t.inferAliasing(&result, schema, isAnonymous, isRequired)
   952  
   953  		result.IsPrimitive = true
   954  		result.IsNullable = nullableString(schema, isRequired)
   955  		result.Extensions = schema.Extensions
   956  
   957  	case object:
   958  		result, err = t.resolveObject(schema, isAnonymous)
   959  		if err != nil {
   960  			result = resolvedType{}
   961  			break
   962  		}
   963  		result.HasDiscriminator = schema.Discriminator != ""
   964  
   965  	case "null":
   966  		if schema.Validations().HasObjectValidations() {
   967  			// no explicit object type, but inferred from object validations:
   968  			// this makes the type a map[string]interface{} instead of interface{}
   969  			result, err = t.resolveObject(schema, isAnonymous)
   970  			if err != nil {
   971  				result = resolvedType{}
   972  				break
   973  			}
   974  			result.HasDiscriminator = schema.Discriminator != ""
   975  			break
   976  		}
   977  
   978  		result.GoType = iface
   979  		result.SwaggerType = object
   980  		result.IsNullable = false
   981  		result.IsInterface = true
   982  
   983  	default:
   984  		err = fmt.Errorf("unresolvable: %v (format %q)", schema.Type, schema.Format)
   985  	}
   986  
   987  	return
   988  }
   989  
   990  func warnSkipValidation(types interface{}) func(string, interface{}) {
   991  	return func(validation string, value interface{}) {
   992  		value = reflect.Indirect(reflect.ValueOf(value)).Interface()
   993  		log.Printf("warning: validation %s (value: %v) not compatible with type %v. Skipped", validation, value, types)
   994  	}
   995  }
   996  
   997  // guardValidations removes (with a warning) validations that don't fit with the schema type.
   998  //
   999  // Notice that the "enum" validation is allowed on any type but file.
  1000  func guardValidations(tpe string, schema interface {
  1001  	Validations() spec.SchemaValidations
  1002  	SetValidations(spec.SchemaValidations)
  1003  }, types ...string) {
  1004  
  1005  	v := schema.Validations()
  1006  	if len(types) == 0 {
  1007  		types = []string{tpe}
  1008  	}
  1009  	defer func() {
  1010  		schema.SetValidations(v)
  1011  	}()
  1012  
  1013  	if tpe != array {
  1014  		v.ClearArrayValidations(warnSkipValidation(types))
  1015  	}
  1016  
  1017  	if tpe != str && tpe != file {
  1018  		v.ClearStringValidations(warnSkipValidation(types))
  1019  	}
  1020  
  1021  	if tpe != object {
  1022  		v.ClearObjectValidations(warnSkipValidation(types))
  1023  	}
  1024  
  1025  	if tpe != number && tpe != integer {
  1026  		v.ClearNumberValidations(warnSkipValidation(types))
  1027  	}
  1028  
  1029  	if tpe == file {
  1030  		// keep MinLength/MaxLength on file
  1031  		if v.Pattern != "" {
  1032  			warnSkipValidation(types)("pattern", v.Pattern)
  1033  			v.Pattern = ""
  1034  		}
  1035  		if v.HasEnum() {
  1036  			warnSkipValidation(types)("enum", v.Enum)
  1037  			v.Enum = nil
  1038  		}
  1039  	}
  1040  
  1041  	// other cases:  mapped as interface{}: no validations allowed but Enum
  1042  }
  1043  
  1044  // guardFormatConflicts handles all conflicting properties
  1045  // (for schema model or simple schema) when a format is set.
  1046  //
  1047  // At this moment, validation guards already handle all known conflicts, but for the
  1048  // special case of binary (i.e. io.Reader).
  1049  func guardFormatConflicts(format string, schema interface {
  1050  	Validations() spec.SchemaValidations
  1051  	SetValidations(spec.SchemaValidations)
  1052  }) {
  1053  	v := schema.Validations()
  1054  	msg := fmt.Sprintf("for format %q", format)
  1055  
  1056  	// for this format, no additional validations are supported
  1057  	if format == "binary" {
  1058  		// no validations supported on binary fields at this moment (io.Reader)
  1059  		v.ClearStringValidations(warnSkipValidation(msg))
  1060  		if v.HasEnum() {
  1061  			warnSkipValidation(msg)
  1062  			v.Enum = nil
  1063  		}
  1064  		schema.SetValidations(v)
  1065  	}
  1066  	// more cases should be inserted here if they arise
  1067  }
  1068  
  1069  // resolvedType is a swagger type that has been resolved and analyzed for usage
  1070  // in a template
  1071  type resolvedType struct {
  1072  	IsAnonymous       bool
  1073  	IsArray           bool
  1074  	IsMap             bool
  1075  	IsInterface       bool
  1076  	IsPrimitive       bool
  1077  	IsCustomFormatter bool
  1078  	IsAliased         bool
  1079  	IsNullable        bool
  1080  	IsStream          bool
  1081  	IsEmptyOmitted    bool
  1082  	IsJSONString      bool
  1083  	IsEnumCI          bool
  1084  	IsBase64          bool
  1085  	IsExternal        bool
  1086  
  1087  	// A tuple gets rendered as an anonymous struct with P{index} as property name
  1088  	IsTuple            bool
  1089  	HasAdditionalItems bool
  1090  
  1091  	// A complex object gets rendered as a struct
  1092  	IsComplexObject bool
  1093  
  1094  	// A polymorphic type
  1095  	IsBaseType       bool
  1096  	HasDiscriminator bool
  1097  
  1098  	GoType        string
  1099  	Pkg           string
  1100  	PkgAlias      string
  1101  	AliasedType   string
  1102  	SwaggerType   string
  1103  	SwaggerFormat string
  1104  	Extensions    spec.Extensions
  1105  
  1106  	// The type of the element in a slice or map
  1107  	ElemType *resolvedType
  1108  
  1109  	// IsMapNullOverride indicates that a nullable object is used within an
  1110  	// aliased map. In this case, the reference is not rendered with a pointer
  1111  	IsMapNullOverride bool
  1112  
  1113  	// IsSuperAlias indicates that the aliased type is really the same type,
  1114  	// e.g. in golang, this translates to: type A = B
  1115  	IsSuperAlias bool
  1116  
  1117  	// IsEmbedded applies to externally defined types. When embedded, a type
  1118  	// is generated in models that embeds the external type, with the Validate
  1119  	// method.
  1120  	IsEmbedded bool
  1121  
  1122  	SkipExternalValidation bool
  1123  }
  1124  
  1125  // Zero returns an initializer for the type
  1126  func (rt resolvedType) Zero() string {
  1127  	// if type is aliased, provide zero from the aliased type
  1128  	if rt.IsAliased {
  1129  		if zr, ok := zeroes[rt.AliasedType]; ok {
  1130  			return rt.GoType + "(" + zr + ")"
  1131  		}
  1132  	}
  1133  	// zero function provided as native or by strfmt function
  1134  	if zr, ok := zeroes[rt.GoType]; ok {
  1135  		return zr
  1136  	}
  1137  	// map and slice initializer
  1138  	if rt.IsMap {
  1139  		return "make(" + rt.GoType + ", 50)"
  1140  	} else if rt.IsArray {
  1141  		return "make(" + rt.GoType + ", 0, 50)"
  1142  	}
  1143  	// object initializer
  1144  	if rt.IsTuple || rt.IsComplexObject {
  1145  		if rt.IsNullable {
  1146  			return "new(" + rt.GoType + ")"
  1147  		}
  1148  		return rt.GoType + "{}"
  1149  	}
  1150  	// interface initializer
  1151  	if rt.IsInterface {
  1152  		return "nil"
  1153  	}
  1154  
  1155  	return ""
  1156  }
  1157  
  1158  // ToString returns a string conversion for a type akin to a string
  1159  func (rt resolvedType) ToString(value string) string {
  1160  	if !rt.IsPrimitive || rt.SwaggerType != "string" || rt.IsStream {
  1161  		return ""
  1162  	}
  1163  	if rt.IsCustomFormatter {
  1164  		if rt.IsAliased {
  1165  			return fmt.Sprintf("%s(%s).String()", rt.AliasedType, value)
  1166  		}
  1167  		return fmt.Sprintf("%s.String()", value)
  1168  	}
  1169  	var deref string
  1170  	if rt.IsNullable {
  1171  		deref = "*"
  1172  	}
  1173  	if rt.GoType == "string" || rt.GoType == "*string" {
  1174  		return fmt.Sprintf("%s%s", deref, value)
  1175  	}
  1176  
  1177  	return fmt.Sprintf("string(%s%s)", deref, value)
  1178  }
  1179  
  1180  func (rt *resolvedType) setExtensions(schema *spec.Schema, origType string) {
  1181  	rt.IsEnumCI = hasEnumCI(schema.Extensions)
  1182  	rt.setIsEmptyOmitted(schema, origType)
  1183  	rt.setIsJSONString(schema, origType)
  1184  
  1185  	if customTag, found := schema.Extensions[xGoCustomTag]; found {
  1186  		if rt.Extensions == nil {
  1187  			rt.Extensions = make(spec.Extensions)
  1188  		}
  1189  		rt.Extensions[xGoCustomTag] = customTag
  1190  	}
  1191  }
  1192  
  1193  func (rt *resolvedType) setIsEmptyOmitted(schema *spec.Schema, tpe string) {
  1194  	if v, found := schema.Extensions[xOmitEmpty]; found {
  1195  		omitted, cast := v.(bool)
  1196  		rt.IsEmptyOmitted = omitted && cast
  1197  		return
  1198  	}
  1199  	// array of primitives are by default not empty-omitted, but arrays of aliased type are
  1200  	rt.IsEmptyOmitted = (tpe != array) || (tpe == array && rt.IsAliased)
  1201  }
  1202  
  1203  func (rt *resolvedType) setIsJSONString(schema *spec.Schema, tpe string) {
  1204  	_, found := schema.Extensions[xGoJSONString]
  1205  	if !found {
  1206  		rt.IsJSONString = false
  1207  		return
  1208  	}
  1209  	rt.IsJSONString = true
  1210  }
  1211  
  1212  func (rt *resolvedType) setKind(kind string) {
  1213  	if kind != "" {
  1214  		debugLog("overriding kind for %s as %s", rt.GoType, kind)
  1215  	}
  1216  	switch kind {
  1217  	case "map":
  1218  		rt.IsMap = true
  1219  		rt.IsArray = false
  1220  		rt.IsComplexObject = false
  1221  		rt.IsInterface = false
  1222  		rt.IsStream = false
  1223  		rt.IsTuple = false
  1224  		rt.IsPrimitive = false
  1225  		rt.SwaggerType = object
  1226  	case "array":
  1227  		rt.IsMap = false
  1228  		rt.IsArray = true
  1229  		rt.IsComplexObject = false
  1230  		rt.IsInterface = false
  1231  		rt.IsStream = false
  1232  		rt.IsTuple = false
  1233  		rt.IsPrimitive = false
  1234  		rt.SwaggerType = array
  1235  	case "object":
  1236  		rt.IsMap = false
  1237  		rt.IsArray = false
  1238  		rt.IsComplexObject = true
  1239  		rt.IsInterface = false
  1240  		rt.IsStream = false
  1241  		rt.IsTuple = false
  1242  		rt.IsPrimitive = false
  1243  		rt.SwaggerType = object
  1244  	case "interface", "null":
  1245  		rt.IsMap = false
  1246  		rt.IsArray = false
  1247  		rt.IsComplexObject = false
  1248  		rt.IsInterface = true
  1249  		rt.IsStream = false
  1250  		rt.IsTuple = false
  1251  		rt.IsPrimitive = false
  1252  		rt.SwaggerType = iface
  1253  	case "stream":
  1254  		rt.IsMap = false
  1255  		rt.IsArray = false
  1256  		rt.IsComplexObject = false
  1257  		rt.IsInterface = false
  1258  		rt.IsStream = true
  1259  		rt.IsTuple = false
  1260  		rt.IsPrimitive = false
  1261  		rt.SwaggerType = file
  1262  	case "tuple":
  1263  		rt.IsMap = false
  1264  		rt.IsArray = false
  1265  		rt.IsComplexObject = false
  1266  		rt.IsInterface = false
  1267  		rt.IsStream = false
  1268  		rt.IsTuple = true
  1269  		rt.IsPrimitive = false
  1270  		rt.SwaggerType = array
  1271  	case "primitive":
  1272  		rt.IsMap = false
  1273  		rt.IsArray = false
  1274  		rt.IsComplexObject = false
  1275  		rt.IsInterface = false
  1276  		rt.IsStream = false
  1277  		rt.IsTuple = false
  1278  		rt.IsPrimitive = true
  1279  	case "":
  1280  		break
  1281  	default:
  1282  		log.Printf("warning: unsupported hint value for external type: %q. Skipped", kind)
  1283  	}
  1284  }