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