github.com/emreu/go-swagger@v0.22.1/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"
    21  	"path/filepath"
    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  )
    29  
    30  const (
    31  	iface   = "interface{}"
    32  	array   = "array"
    33  	file    = "file"
    34  	number  = "number"
    35  	integer = "integer"
    36  	boolean = "boolean"
    37  	str     = "string"
    38  	object  = "object"
    39  	binary  = "binary"
    40  	sHTTP   = "http"
    41  	body    = "body"
    42  )
    43  
    44  // Extensions supported by go-swagger
    45  const (
    46  	xClass       = "x-class"         // class name used by discriminator
    47  	xGoCustomTag = "x-go-custom-tag" // additional tag for serializers on struct fields
    48  	xGoName      = "x-go-name"       // name of the generated go variable
    49  	xGoType      = "x-go-type"       // reuse existing type (do not generate)
    50  	xIsNullable  = "x-isnullable"
    51  	xNullable    = "x-nullable" // turns the schema into a pointer
    52  	xOmitEmpty   = "x-omitempty"
    53  	xSchemes     = "x-schemes" // additional schemes supported for operations (server generation)
    54  	xOrder       = "x-order"   // sort order for properties (or any schema)
    55  )
    56  
    57  // swaggerTypeMapping contains a mapping from go type to swagger type or format
    58  var swaggerTypeName map[string]string
    59  
    60  func init() {
    61  	swaggerTypeName = make(map[string]string)
    62  	for k, v := range typeMapping {
    63  		swaggerTypeName[v] = k
    64  	}
    65  }
    66  
    67  func simpleResolvedType(tn, fmt string, items *spec.Items) (result resolvedType) {
    68  	result.SwaggerType = tn
    69  	result.SwaggerFormat = fmt
    70  
    71  	if tn == file {
    72  		// special case of swagger type "file", rendered as io.ReadCloser interface
    73  		result.IsPrimitive = true
    74  		result.GoType = formatMapping[str][binary]
    75  		result.IsStream = true
    76  		return
    77  	}
    78  
    79  	if fmt != "" {
    80  		fmtn := strings.Replace(fmt, "-", "", -1)
    81  		if fmm, ok := formatMapping[tn]; ok {
    82  			if tpe, ok := fmm[fmtn]; ok {
    83  				result.GoType = tpe
    84  				result.IsPrimitive = true
    85  				_, result.IsCustomFormatter = customFormatters[tpe]
    86  				// special case of swagger format "binary", rendered as io.ReadCloser interface
    87  				// TODO(fredbi): should set IsCustomFormatter=false when binary
    88  				result.IsStream = fmt == binary
    89  				return
    90  			}
    91  		}
    92  	}
    93  
    94  	if tpe, ok := typeMapping[tn]; ok {
    95  		result.GoType = tpe
    96  		_, result.IsPrimitive = primitives[tpe]
    97  		result.IsPrimitive = ok
    98  		return
    99  	}
   100  
   101  	if tn == array {
   102  		result.IsArray = true
   103  		result.IsPrimitive = false
   104  		result.IsCustomFormatter = false
   105  		result.IsNullable = false
   106  		if items == nil {
   107  			result.GoType = "[]" + iface
   108  			return
   109  		}
   110  		res := simpleResolvedType(items.Type, items.Format, items.Items)
   111  		result.GoType = "[]" + res.GoType
   112  		return
   113  	}
   114  	result.GoType = tn
   115  	_, result.IsPrimitive = primitives[tn]
   116  	return
   117  }
   118  
   119  func typeForHeader(header spec.Header) resolvedType {
   120  	return simpleResolvedType(header.Type, header.Format, header.Items)
   121  }
   122  
   123  func newTypeResolver(pkg string, doc *loads.Document) *typeResolver {
   124  	resolver := typeResolver{ModelsPackage: pkg, Doc: doc}
   125  	resolver.KnownDefs = make(map[string]struct{}, len(doc.Spec().Definitions))
   126  	for k, sch := range doc.Spec().Definitions {
   127  		tpe, _, _ := knownDefGoType(k, sch, nil)
   128  		resolver.KnownDefs[tpe] = struct{}{}
   129  	}
   130  	return &resolver
   131  }
   132  
   133  // knownDefGoType returns go type, package and package alias for definition
   134  func knownDefGoType(def string, schema spec.Schema, clear func(string) string) (string, string, string) {
   135  	debugLog("known def type: %q", def)
   136  	ext := schema.Extensions
   137  	if nm, ok := ext.GetString(xGoName); ok {
   138  		if clear == nil {
   139  			debugLog("known def type %s no clear: %q", xGoName, nm)
   140  			return nm, "", ""
   141  		}
   142  		debugLog("known def type %s clear: %q -> %q", xGoName, nm, clear(nm))
   143  		return clear(nm), "", ""
   144  	}
   145  	v, ok := ext[xGoType]
   146  	if !ok {
   147  		if clear == nil {
   148  			debugLog("known def type no clear: %q", def)
   149  			return def, "", ""
   150  		}
   151  		debugLog("known def type clear: %q -> %q", def, clear(def))
   152  		return clear(def), "", ""
   153  	}
   154  	xt := v.(map[string]interface{})
   155  	t := xt["type"].(string)
   156  	impIface, ok := xt["import"]
   157  
   158  	if !ok {
   159  		return t, "", ""
   160  	}
   161  
   162  	imp := impIface.(map[string]interface{})
   163  	pkg := imp["package"].(string)
   164  	al, ok := imp["alias"]
   165  	var alias string
   166  	if ok {
   167  		alias = al.(string)
   168  	} else {
   169  		alias = path.Base(pkg)
   170  	}
   171  	debugLog("known def type %s no clear: %q: pkg=%s, alias=%s", xGoType, alias+"."+t, pkg, alias)
   172  	return alias + "." + t, pkg, alias
   173  }
   174  
   175  type typeResolver struct {
   176  	Doc           *loads.Document
   177  	ModelsPackage string
   178  	ModelName     string
   179  	KnownDefs     map[string]struct{}
   180  	// unexported fields
   181  	keepDefinitionsPkg string
   182  	knownDefsKept      map[string]struct{}
   183  }
   184  
   185  // NewWithModelName clones a type resolver and specifies a new model name
   186  func (t *typeResolver) NewWithModelName(name string) *typeResolver {
   187  	tt := newTypeResolver(t.ModelsPackage, t.Doc)
   188  	tt.ModelName = name
   189  
   190  	// propagates kept definitions
   191  	tt.keepDefinitionsPkg = t.keepDefinitionsPkg
   192  	tt.knownDefsKept = t.knownDefsKept
   193  	return tt
   194  }
   195  
   196  // withKeepDefinitionsPackage instructs the type resolver to keep previously resolved package name for
   197  // definitions known at the moment it is first called.
   198  func (t *typeResolver) withKeepDefinitionsPackage(definitionsPackage string) *typeResolver {
   199  	t.keepDefinitionsPkg = definitionsPackage
   200  	t.knownDefsKept = make(map[string]struct{}, len(t.KnownDefs))
   201  	for k := range t.KnownDefs {
   202  		t.knownDefsKept[k] = struct{}{}
   203  	}
   204  	return t
   205  }
   206  
   207  // IsNullable hints the generator as to render the type with a pointer or not.
   208  //
   209  // A schema is deemed nullable (i.e. rendered by a pointer) when:
   210  // - a custom extension says it has to be so
   211  // - it is an object with properties
   212  // - it is a composed object (allOf)
   213  //
   214  // The interpretation of Required as a mean to make a type nullable is carried on elsewhere.
   215  func (t *typeResolver) IsNullable(schema *spec.Schema) bool {
   216  	nullable := t.isNullable(schema)
   217  	return nullable || len(schema.AllOf) > 0
   218  }
   219  
   220  func (t *typeResolver) resolveSchemaRef(schema *spec.Schema, isRequired bool) (returns bool, result resolvedType, err error) {
   221  	if schema.Ref.String() != "" {
   222  		debugLog("resolving ref (anon: %t, req: %t) %s", false, isRequired, schema.Ref.String())
   223  		returns = true
   224  		var ref *spec.Schema
   225  		var er error
   226  
   227  		ref, er = spec.ResolveRef(t.Doc.Spec(), &schema.Ref)
   228  		if er != nil {
   229  			debugLog("error resolving ref %s: %v", schema.Ref.String(), er)
   230  			err = er
   231  			return
   232  		}
   233  		res, er := t.ResolveSchema(ref, false, isRequired)
   234  		if er != nil {
   235  			err = er
   236  			return
   237  		}
   238  		result = res
   239  
   240  		tn := filepath.Base(schema.Ref.GetURL().Fragment)
   241  		tpe, pkg, alias := knownDefGoType(tn, *ref, t.goTypeName)
   242  		debugLog("type name %s, package %s, alias %s", tpe, pkg, alias)
   243  		if tpe != "" {
   244  			result.GoType = tpe
   245  			result.Pkg = pkg
   246  			result.PkgAlias = alias
   247  		}
   248  		result.HasDiscriminator = res.HasDiscriminator
   249  		result.IsBaseType = result.HasDiscriminator
   250  		result.IsNullable = t.IsNullable(ref)
   251  		//result.IsAliased = true
   252  		return
   253  
   254  	}
   255  	return
   256  }
   257  
   258  func (t *typeResolver) inferAliasing(result *resolvedType, schema *spec.Schema, isAnonymous bool, isRequired bool) {
   259  	if !isAnonymous && t.ModelName != "" {
   260  		result.AliasedType = result.GoType
   261  		result.IsAliased = true
   262  		result.GoType = t.goTypeName(t.ModelName)
   263  	}
   264  }
   265  
   266  func (t *typeResolver) resolveFormat(schema *spec.Schema, isAnonymous bool, isRequired bool) (returns bool, result resolvedType, err error) {
   267  
   268  	if schema.Format != "" {
   269  		// defaults to string
   270  		result.SwaggerType = str
   271  		if len(schema.Type) > 0 {
   272  			result.SwaggerType = schema.Type[0]
   273  		}
   274  
   275  		debugLog("resolving format (anon: %t, req: %t)", isAnonymous, isRequired)
   276  		schFmt := strings.Replace(schema.Format, "-", "", -1)
   277  		if fmm, ok := formatMapping[result.SwaggerType]; ok {
   278  			if tpe, ok := fmm[schFmt]; ok {
   279  				returns = true
   280  				result.GoType = tpe
   281  				_, result.IsCustomFormatter = customFormatters[tpe]
   282  			}
   283  		}
   284  		if tpe, ok := typeMapping[schFmt]; !returns && ok {
   285  			returns = true
   286  			result.GoType = tpe
   287  			_, result.IsCustomFormatter = customFormatters[tpe]
   288  		}
   289  
   290  		result.SwaggerFormat = schema.Format
   291  		t.inferAliasing(&result, schema, isAnonymous, isRequired)
   292  		// special case of swagger format "binary", rendered as io.ReadCloser interface and is therefore not a primitive type
   293  		// TODO: should set IsCustomFormatter=false in this case.
   294  		result.IsPrimitive = schFmt != binary
   295  		result.IsStream = schFmt == binary
   296  		// propagate extensions in resolvedType
   297  		result.Extensions = schema.Extensions
   298  
   299  		switch result.SwaggerType {
   300  		case str:
   301  			result.IsNullable = nullableStrfmt(schema, isRequired)
   302  		case number, integer:
   303  			result.IsNullable = nullableNumber(schema, isRequired)
   304  		default:
   305  			result.IsNullable = t.IsNullable(schema)
   306  		}
   307  	}
   308  	return
   309  }
   310  
   311  func (t *typeResolver) isNullable(schema *spec.Schema) bool {
   312  	check := func(extension string) (bool, bool) {
   313  		v, found := schema.Extensions[extension]
   314  		nullable, cast := v.(bool)
   315  		return nullable, found && cast
   316  	}
   317  
   318  	if nullable, ok := check(xIsNullable); ok {
   319  		return nullable
   320  	}
   321  	if nullable, ok := check(xNullable); ok {
   322  		return nullable
   323  	}
   324  	return len(schema.Properties) > 0
   325  }
   326  
   327  func setIsEmptyOmitted(result *resolvedType, schema *spec.Schema, tpe string) {
   328  	defaultValue := true
   329  	if tpe == array {
   330  		defaultValue = false
   331  	}
   332  	v, found := schema.Extensions[xOmitEmpty]
   333  	if !found {
   334  		result.IsEmptyOmitted = defaultValue
   335  		return
   336  	}
   337  
   338  	omitted, cast := v.(bool)
   339  	result.IsEmptyOmitted = omitted && cast
   340  }
   341  
   342  func (t *typeResolver) firstType(schema *spec.Schema) string {
   343  	if len(schema.Type) == 0 || schema.Type[0] == "" {
   344  		return object
   345  	}
   346  	if len(schema.Type) > 1 {
   347  		// JSON-Schema multiple types, e.g. {"type": [ "object", "array" ]} are not supported.
   348  		// TODO: should keep the first _supported_ type, e.g. skip null
   349  		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])
   350  	}
   351  	return schema.Type[0]
   352  }
   353  
   354  func (t *typeResolver) resolveArray(schema *spec.Schema, isAnonymous, isRequired bool) (result resolvedType, err error) {
   355  	debugLog("resolving array (anon: %t, req: %t)", isAnonymous, isRequired)
   356  
   357  	result.IsArray = true
   358  	result.IsNullable = false
   359  
   360  	if schema.AdditionalItems != nil {
   361  		result.HasAdditionalItems = (schema.AdditionalItems.Allows || schema.AdditionalItems.Schema != nil)
   362  	}
   363  
   364  	if schema.Items == nil {
   365  		result.GoType = "[]" + iface
   366  		result.SwaggerType = array
   367  		result.SwaggerFormat = ""
   368  		t.inferAliasing(&result, schema, isAnonymous, isRequired)
   369  
   370  		return
   371  	}
   372  
   373  	if len(schema.Items.Schemas) > 0 {
   374  		result.IsArray = false
   375  		result.IsTuple = true
   376  		result.SwaggerType = array
   377  		result.SwaggerFormat = ""
   378  		t.inferAliasing(&result, schema, isAnonymous, isRequired)
   379  
   380  		return
   381  	}
   382  
   383  	rt, er := t.ResolveSchema(schema.Items.Schema, true, false)
   384  	if er != nil {
   385  		err = er
   386  		return
   387  	}
   388  	// override the general nullability rule from ResolveSchema():
   389  	// only complex items are nullable (when not discriminated, not forced by x-nullable)
   390  	rt.IsNullable = t.IsNullable(schema.Items.Schema) && !rt.HasDiscriminator
   391  	result.GoType = "[]" + rt.GoType
   392  	if rt.IsNullable && !strings.HasPrefix(rt.GoType, "*") {
   393  		result.GoType = "[]*" + rt.GoType
   394  	}
   395  
   396  	result.ElemType = &rt
   397  	result.SwaggerType = array
   398  	result.SwaggerFormat = ""
   399  	t.inferAliasing(&result, schema, isAnonymous, isRequired)
   400  	result.Extensions = schema.Extensions
   401  
   402  	return
   403  }
   404  
   405  func (t *typeResolver) goTypeName(nm string) string {
   406  	if len(t.knownDefsKept) > 0 {
   407  		// if a definitions package has been defined, already resolved definitions are
   408  		// always resolved against their original package (e.g. "models"), and not the
   409  		// current package.
   410  		// This allows complex anonymous extra schemas to reuse known definitions generated in another package.
   411  		if _, ok := t.knownDefsKept[nm]; ok {
   412  			return strings.Join([]string{t.keepDefinitionsPkg, swag.ToGoName(nm)}, ".")
   413  		}
   414  	}
   415  
   416  	if t.ModelsPackage == "" {
   417  		return swag.ToGoName(nm)
   418  	}
   419  	if _, ok := t.KnownDefs[nm]; ok {
   420  		return strings.Join([]string{t.ModelsPackage, swag.ToGoName(nm)}, ".")
   421  	}
   422  	return swag.ToGoName(nm)
   423  }
   424  
   425  func (t *typeResolver) resolveObject(schema *spec.Schema, isAnonymous bool) (result resolvedType, err error) {
   426  	debugLog("resolving object %s (anon: %t, req: %t)", t.ModelName, isAnonymous, false)
   427  
   428  	result.IsAnonymous = isAnonymous
   429  
   430  	result.IsBaseType = schema.Discriminator != ""
   431  	if !isAnonymous {
   432  		result.SwaggerType = object
   433  		tpe, pkg, alias := knownDefGoType(t.ModelName, *schema, t.goTypeName)
   434  		result.GoType = tpe
   435  		result.Pkg = pkg
   436  		result.PkgAlias = alias
   437  	}
   438  	if len(schema.AllOf) > 0 {
   439  		result.GoType = t.goTypeName(t.ModelName)
   440  		result.IsComplexObject = true
   441  		var isNullable bool
   442  		for _, p := range schema.AllOf {
   443  			if t.IsNullable(&p) {
   444  				isNullable = true
   445  			}
   446  		}
   447  		result.IsNullable = isNullable
   448  		result.SwaggerType = object
   449  		return
   450  	}
   451  
   452  	// if this schema has properties, build a map of property name to
   453  	// resolved type, this should also flag the object as anonymous,
   454  	// when a ref is found, the anonymous flag will be reset
   455  	if len(schema.Properties) > 0 {
   456  		result.IsNullable = t.IsNullable(schema)
   457  		result.IsComplexObject = true
   458  		// no return here, still need to check for additional properties
   459  	}
   460  
   461  	// account for additional properties
   462  	if schema.AdditionalProperties != nil && schema.AdditionalProperties.Schema != nil {
   463  		sch := schema.AdditionalProperties.Schema
   464  		et, er := t.ResolveSchema(sch, sch.Ref.String() == "", false)
   465  		if er != nil {
   466  			err = er
   467  			return
   468  		}
   469  
   470  		result.IsMap = !result.IsComplexObject
   471  
   472  		result.SwaggerType = object
   473  
   474  		// only complex map elements are nullable (when not forced by x-nullable)
   475  		// TODO: figure out if required to check when not discriminated like arrays?
   476  		et.IsNullable = t.isNullable(schema.AdditionalProperties.Schema)
   477  		if et.IsNullable {
   478  			result.GoType = "map[string]*" + et.GoType
   479  		} else {
   480  			result.GoType = "map[string]" + et.GoType
   481  		}
   482  
   483  		// Resolving nullability conflicts for:
   484  		// - map[][]...[]{items}
   485  		// - map[]{aliased type}
   486  		//
   487  		// when IsMap is true and the type is a distinct definition,
   488  		// aliased type or anonymous construct generated independently.
   489  		//
   490  		// IsMapNullOverride is to be handled by the generator for special cases
   491  		// where the map element is considered non nullable and the element itself is.
   492  		//
   493  		// This allows to appreciate nullability according to the context
   494  		needsOverride := result.IsMap && (et.IsArray || (sch.Ref.String() != "" || et.IsAliased || et.IsAnonymous))
   495  
   496  		if needsOverride {
   497  			var er error
   498  			if et.IsArray {
   499  				var it resolvedType
   500  				s := sch
   501  				// resolve the last items after nested arrays
   502  				for s.Items != nil && s.Items.Schema != nil {
   503  					it, er = t.ResolveSchema(s.Items.Schema, sch.Ref.String() == "", false)
   504  					if er != nil {
   505  						return
   506  					}
   507  					s = s.Items.Schema
   508  				}
   509  				// mark an override when nullable status conflicts, i.e. when the original type is not already nullable
   510  				if !it.IsAnonymous || it.IsAnonymous && it.IsNullable {
   511  					result.IsMapNullOverride = true
   512  				}
   513  			} else {
   514  				// this locks the generator on the local nullability status
   515  				result.IsMapNullOverride = true
   516  			}
   517  		}
   518  
   519  		t.inferAliasing(&result, schema, isAnonymous, false)
   520  		result.ElemType = &et
   521  		return
   522  	}
   523  
   524  	if len(schema.Properties) > 0 {
   525  		return
   526  	}
   527  
   528  	// an object without property and without AdditionalProperties schema is rendered as interface{}
   529  	result.GoType = iface
   530  	result.IsMap = true
   531  	result.SwaggerType = object
   532  	result.IsNullable = false
   533  	result.IsInterface = len(schema.Properties) == 0
   534  	return
   535  }
   536  
   537  // nullableBool makes a boolean a pointer when we want to distinguish the zero value from no value set.
   538  // This is the case when:
   539  // - a x-nullable extension says so in the spec
   540  // - it is **not** a read-only property
   541  // - it is a required property
   542  // - it has a default value
   543  func nullableBool(schema *spec.Schema, isRequired bool) bool {
   544  	if nullable := nullableExtension(schema.Extensions); nullable != nil {
   545  		return *nullable
   546  	}
   547  	required := isRequired && schema.Default == nil && !schema.ReadOnly
   548  	optional := !isRequired && (schema.Default != nil || schema.ReadOnly)
   549  
   550  	return required || optional
   551  }
   552  
   553  // nullableNumber makes a number a pointer when we want to distinguish the zero value from no value set.
   554  // This is the case when:
   555  // - a x-nullable extension says so in the spec
   556  // - it is **not** a read-only property
   557  // - it is a required property
   558  // - boundaries defines the zero value as a valid value:
   559  //   - there is a non-exclusive boundary set at the zero value of the type
   560  //   - the [min,max] range crosses the zero value of the type
   561  func nullableNumber(schema *spec.Schema, isRequired bool) bool {
   562  	if nullable := nullableExtension(schema.Extensions); nullable != nil {
   563  		return *nullable
   564  	}
   565  	hasDefault := schema.Default != nil && !swag.IsZero(schema.Default)
   566  
   567  	isMin := schema.Minimum != nil && (*schema.Minimum != 0 || schema.ExclusiveMinimum)
   568  	bcMin := schema.Minimum != nil && *schema.Minimum == 0 && !schema.ExclusiveMinimum
   569  	isMax := schema.Minimum == nil && (schema.Maximum != nil && (*schema.Maximum != 0 || schema.ExclusiveMaximum))
   570  	bcMax := schema.Maximum != nil && *schema.Maximum == 0 && !schema.ExclusiveMaximum
   571  	isMinMax := (schema.Minimum != nil && schema.Maximum != nil && *schema.Minimum < *schema.Maximum)
   572  	bcMinMax := (schema.Minimum != nil && schema.Maximum != nil && (*schema.Minimum < 0 && 0 < *schema.Maximum))
   573  
   574  	nullable := !schema.ReadOnly && (isRequired || (hasDefault && !(isMin || isMax || isMinMax)) || bcMin || bcMax || bcMinMax)
   575  	return nullable
   576  }
   577  
   578  // nullableString makes a string nullable when we want to distinguish the zero value from no value set.
   579  // This is the case when:
   580  // - a x-nullable extension says so in the spec
   581  // - it is **not** a read-only property
   582  // - it is a required property
   583  // - it has a MinLength property set to 0
   584  // - it has a default other than "" (the zero for strings) and no MinLength or zero MinLength
   585  func nullableString(schema *spec.Schema, isRequired bool) bool {
   586  	if nullable := nullableExtension(schema.Extensions); nullable != nil {
   587  		return *nullable
   588  	}
   589  	hasDefault := schema.Default != nil && !swag.IsZero(schema.Default)
   590  
   591  	isMin := schema.MinLength != nil && *schema.MinLength != 0
   592  	bcMin := schema.MinLength != nil && *schema.MinLength == 0
   593  
   594  	nullable := !schema.ReadOnly && (isRequired || (hasDefault && !isMin) || bcMin)
   595  	return nullable
   596  }
   597  
   598  func nullableStrfmt(schema *spec.Schema, isRequired bool) bool {
   599  	notBinary := schema.Format != binary
   600  	if nullable := nullableExtension(schema.Extensions); nullable != nil && notBinary {
   601  		return *nullable
   602  	}
   603  	hasDefault := schema.Default != nil && !swag.IsZero(schema.Default)
   604  
   605  	nullable := !schema.ReadOnly && (isRequired || hasDefault)
   606  	return notBinary && nullable
   607  }
   608  
   609  func nullableExtension(ext spec.Extensions) *bool {
   610  	if ext == nil {
   611  		return nil
   612  	}
   613  
   614  	if boolPtr := boolExtension(ext, xNullable); boolPtr != nil {
   615  		return boolPtr
   616  	}
   617  
   618  	return boolExtension(ext, xIsNullable)
   619  }
   620  
   621  func boolExtension(ext spec.Extensions, key string) *bool {
   622  	if v, ok := ext[key]; ok {
   623  		if bb, ok := v.(bool); ok {
   624  			return &bb
   625  		}
   626  	}
   627  	return nil
   628  }
   629  
   630  func (t *typeResolver) ResolveSchema(schema *spec.Schema, isAnonymous, isRequired bool) (result resolvedType, err error) {
   631  	debugLog("resolving schema (anon: %t, req: %t) %s", isAnonymous, isRequired, t.ModelName)
   632  	if schema == nil {
   633  		result.IsInterface = true
   634  		result.GoType = iface
   635  		return
   636  	}
   637  
   638  	tpe := t.firstType(schema)
   639  	defer setIsEmptyOmitted(&result, schema, tpe)
   640  
   641  	var returns bool
   642  	returns, result, err = t.resolveSchemaRef(schema, isRequired)
   643  	if returns {
   644  		if !isAnonymous {
   645  			result.IsMap = false
   646  			result.IsComplexObject = true
   647  			debugLog("not anonymous ref")
   648  		}
   649  		debugLog("returning after ref")
   650  		return
   651  	}
   652  
   653  	// special case of swagger type "file", rendered as io.ReadCloser interface
   654  	if t.firstType(schema) == file {
   655  		result.SwaggerType = file
   656  		result.IsPrimitive = true
   657  		result.IsNullable = false
   658  		result.GoType = formatMapping[str][binary]
   659  		result.IsStream = true
   660  		return
   661  	}
   662  
   663  	returns, result, err = t.resolveFormat(schema, isAnonymous, isRequired)
   664  	if returns {
   665  		debugLog("returning after resolve format: %s", pretty.Sprint(result))
   666  		return
   667  	}
   668  
   669  	result.IsNullable = t.isNullable(schema) || isRequired
   670  
   671  	switch tpe {
   672  	case array:
   673  		result, err = t.resolveArray(schema, isAnonymous, false)
   674  		return
   675  
   676  	case file, number, integer, boolean:
   677  		result.Extensions = schema.Extensions
   678  		result.GoType = typeMapping[tpe]
   679  		result.SwaggerType = tpe
   680  		t.inferAliasing(&result, schema, isAnonymous, isRequired)
   681  
   682  		switch tpe {
   683  		case boolean:
   684  			result.IsPrimitive = true
   685  			result.IsCustomFormatter = false
   686  			result.IsNullable = nullableBool(schema, isRequired)
   687  		case number, integer:
   688  			result.IsPrimitive = true
   689  			result.IsCustomFormatter = false
   690  			result.IsNullable = nullableNumber(schema, isRequired)
   691  		case file:
   692  		}
   693  		return
   694  
   695  	case str:
   696  		result.GoType = str
   697  		result.SwaggerType = str
   698  		t.inferAliasing(&result, schema, isAnonymous, isRequired)
   699  
   700  		result.IsPrimitive = true
   701  		result.IsNullable = nullableString(schema, isRequired)
   702  		result.Extensions = schema.Extensions
   703  
   704  	case object:
   705  		result, err = t.resolveObject(schema, isAnonymous)
   706  		if err != nil {
   707  			return resolvedType{}, err
   708  		}
   709  		result.HasDiscriminator = schema.Discriminator != ""
   710  		return
   711  
   712  	case "null":
   713  		result.GoType = iface
   714  		result.SwaggerType = object
   715  		result.IsNullable = false
   716  		result.IsInterface = true
   717  		return
   718  
   719  	default:
   720  		err = fmt.Errorf("unresolvable: %v (format %q)", schema.Type, schema.Format)
   721  		return
   722  	}
   723  	return result, err
   724  }
   725  
   726  // resolvedType is a swagger type that has been resolved and analyzed for usage
   727  // in a template
   728  type resolvedType struct {
   729  	IsAnonymous       bool
   730  	IsArray           bool
   731  	IsMap             bool
   732  	IsInterface       bool
   733  	IsPrimitive       bool
   734  	IsCustomFormatter bool
   735  	IsAliased         bool
   736  	IsNullable        bool
   737  	IsStream          bool
   738  	IsEmptyOmitted    bool
   739  
   740  	// A tuple gets rendered as an anonymous struct with P{index} as property name
   741  	IsTuple            bool
   742  	HasAdditionalItems bool
   743  
   744  	// A complex object gets rendered as a struct
   745  	IsComplexObject bool
   746  
   747  	// A polymorphic type
   748  	IsBaseType       bool
   749  	HasDiscriminator bool
   750  
   751  	GoType        string
   752  	Pkg           string
   753  	PkgAlias      string
   754  	AliasedType   string
   755  	SwaggerType   string
   756  	SwaggerFormat string
   757  	Extensions    spec.Extensions
   758  
   759  	// The type of the element in a slice or map
   760  	ElemType *resolvedType
   761  
   762  	// IsMapNullOverride indicates that a nullable object is used within an
   763  	// aliased map. In this case, the reference is not rendered with a pointer
   764  	IsMapNullOverride bool
   765  
   766  	// IsSuperAlias indicates that the aliased type is really the same type,
   767  	// e.g. in golang, this translates to: type A = B
   768  	IsSuperAlias bool
   769  }
   770  
   771  func (rt *resolvedType) Zero() string {
   772  	// if type is aliased, provide zero from the aliased type
   773  	if rt.IsAliased {
   774  		if zr, ok := zeroes[rt.AliasedType]; ok {
   775  			return rt.GoType + "(" + zr + ")"
   776  		}
   777  	}
   778  	// zero function provided as native or by strfmt function
   779  	if zr, ok := zeroes[rt.GoType]; ok {
   780  		return zr
   781  	}
   782  	// map and slice initializer
   783  	if rt.IsMap {
   784  		return "make(" + rt.GoType + ", 50)"
   785  	} else if rt.IsArray {
   786  		return "make(" + rt.GoType + ", 0, 50)"
   787  	}
   788  	// object initializer
   789  	if rt.IsTuple || rt.IsComplexObject {
   790  		if rt.IsNullable {
   791  			return "new(" + rt.GoType + ")"
   792  		}
   793  		return rt.GoType + "{}"
   794  	}
   795  	// interface initializer
   796  	if rt.IsInterface {
   797  		return "nil"
   798  	}
   799  
   800  	return ""
   801  }