github.com/Desuuuu/genqlient@v0.5.3/generate/types.go (about) 1 package generate 2 3 // This file defines the data structures from which genqlient generates types, 4 // and the code to write them out as actual Go code. The main entrypoint is 5 // goType, which represents such a type, but convert.go also constructs each 6 // of the implementing types, by traversing the GraphQL operation and schema. 7 8 import ( 9 "fmt" 10 "io" 11 "strings" 12 13 "github.com/vektah/gqlparser/v2/ast" 14 ) 15 16 // goType represents a type for which we'll generate code. 17 type goType interface { 18 // WriteDefinition writes the code for this type into the given io.Writer. 19 // 20 // TODO(benkraft): Some of the implementations might now benefit from being 21 // converted to templates. 22 WriteDefinition(io.Writer, *generator) error 23 24 // Reference returns the Go name of this type, e.g. []*MyStruct, and may be 25 // used to refer to it in Go code. 26 Reference() string 27 28 // GraphQLTypeName returns the name of the GraphQL type to which this Go type 29 // corresponds. 30 GraphQLTypeName() string 31 32 // SelectionSet returns the selection-set of the GraphQL field from which 33 // this type was generated, or nil if none is applicable (for GraphQL 34 // scalar, enum, and input types, as well as any opaque 35 // (non-genqlient-generated) type since those are validated upon creation). 36 SelectionSet() ast.SelectionSet 37 38 // Remove slice/pointer wrappers, and return the underlying (named (or 39 // builtin)) type. For example, given []*MyStruct, return MyStruct. 40 Unwrap() goType 41 42 // Count the number of times Unwrap() will unwrap a slice type. For 43 // example, given [][][]*MyStruct (or []**[][]*MyStruct, but we never 44 // currently generate that), return 3. 45 SliceDepth() int 46 47 // True if Unwrap() will unwrap a pointer at least once. 48 IsPointer() bool 49 } 50 51 var ( 52 _ goType = (*goOpaqueType)(nil) 53 _ goType = (*goSliceType)(nil) 54 _ goType = (*goPointerType)(nil) 55 _ goType = (*goEnumType)(nil) 56 _ goType = (*goStructType)(nil) 57 _ goType = (*goInterfaceType)(nil) 58 ) 59 60 type ( 61 // goOpaqueType represents a user-defined or builtin type, often used to 62 // represent a GraphQL scalar. (See Config.Bindings for more context.) 63 goOpaqueType struct { 64 GoRef string 65 GraphQLName string 66 Marshaler, Unmarshaler string 67 } 68 // goTypenameForBuiltinType represents a builtin type that was 69 // given a different name due to a `typename` directive. We 70 // create a type like `type MyString string` for it. 71 goTypenameForBuiltinType struct { 72 GoTypeName string 73 GoBuiltinName string 74 GraphQLName string 75 } 76 // goSliceType represents the Go type []Elem, used to represent GraphQL 77 // list types. 78 goSliceType struct{ Elem goType } 79 // goSliceType represents the Go type *Elem, used when requested by the 80 // user (perhaps to handle nulls explicitly, or to avoid copying large 81 // structures). 82 goPointerType struct{ Elem goType } 83 ) 84 85 // Opaque types are defined by the user; pointers and slices need no definition 86 func (typ *goOpaqueType) WriteDefinition(io.Writer, *generator) error { return nil } 87 88 func (typ *goTypenameForBuiltinType) WriteDefinition(w io.Writer, g *generator) error { 89 fmt.Fprintf(w, "type %s %s", typ.GoTypeName, typ.GoBuiltinName) 90 return nil 91 } 92 func (typ *goSliceType) WriteDefinition(io.Writer, *generator) error { return nil } 93 func (typ *goPointerType) WriteDefinition(io.Writer, *generator) error { return nil } 94 95 func (typ *goOpaqueType) Reference() string { return typ.GoRef } 96 func (typ *goTypenameForBuiltinType) Reference() string { return typ.GoTypeName } 97 func (typ *goSliceType) Reference() string { return "[]" + typ.Elem.Reference() } 98 func (typ *goPointerType) Reference() string { return "*" + typ.Elem.Reference() } 99 100 func (typ *goOpaqueType) SelectionSet() ast.SelectionSet { return nil } 101 func (typ *goTypenameForBuiltinType) SelectionSet() ast.SelectionSet { return nil } 102 func (typ *goSliceType) SelectionSet() ast.SelectionSet { return typ.Elem.SelectionSet() } 103 func (typ *goPointerType) SelectionSet() ast.SelectionSet { return typ.Elem.SelectionSet() } 104 105 func (typ *goOpaqueType) GraphQLTypeName() string { return typ.GraphQLName } 106 func (typ *goTypenameForBuiltinType) GraphQLTypeName() string { return typ.GraphQLName } 107 func (typ *goSliceType) GraphQLTypeName() string { return typ.Elem.GraphQLTypeName() } 108 func (typ *goPointerType) GraphQLTypeName() string { return typ.Elem.GraphQLTypeName() } 109 110 // goEnumType represents a Go named-string type used to represent a GraphQL 111 // enum. In this case, we generate both the type (`type T string`) and also a 112 // list of consts representing the values. 113 type goEnumType struct { 114 GoName string 115 GraphQLName string 116 Description string 117 Values []goEnumValue 118 } 119 120 type goEnumValue struct { 121 Name string 122 Description string 123 } 124 125 func (typ *goEnumType) WriteDefinition(w io.Writer, g *generator) error { 126 // All GraphQL enums have underlying type string (in the Go sense). 127 writeDescription(w, typ.Description) 128 fmt.Fprintf(w, "type %s string\n", typ.GoName) 129 fmt.Fprintf(w, "const (\n") 130 for _, val := range typ.Values { 131 writeDescription(w, val.Description) 132 fmt.Fprintf(w, "%s %s = \"%s\"\n", 133 typ.GoName+goConstName(val.Name), 134 typ.GoName, val.Name) 135 } 136 fmt.Fprintf(w, ")\n") 137 return nil 138 } 139 140 func (typ *goEnumType) Reference() string { return typ.GoName } 141 func (typ *goEnumType) SelectionSet() ast.SelectionSet { return nil } 142 func (typ *goEnumType) GraphQLTypeName() string { return typ.GraphQLName } 143 144 // goStructType represents a Go struct type used to represent a GraphQL object 145 // or input-object type. 146 type goStructType struct { 147 GoName string 148 Fields []*goStructField 149 IsInput bool 150 Selection ast.SelectionSet 151 descriptionInfo 152 Generator *generator // for the convenience of the template 153 } 154 155 type goStructField struct { 156 GoName string 157 GoType goType 158 JSONName string // i.e. the field's alias in this query 159 GraphQLName string // i.e. the field's name in its type-def 160 Omitempty bool // only used on input types 161 Description string 162 } 163 164 // IsAbstract returns true if this field is of abstract type (i.e. GraphQL 165 // union or interface; equivalently, represented by an interface in Go). 166 func (field *goStructField) IsAbstract() bool { 167 _, ok := field.GoType.Unwrap().(*goInterfaceType) 168 return ok 169 } 170 171 // IsEmbedded returns true if this field is embedded (a.k.a. anonymous), which 172 // is in practice true if it corresponds to a named fragment spread in GraphQL. 173 func (field *goStructField) IsEmbedded() bool { 174 return field.GoName == "" 175 } 176 177 // Selector returns the field's name, which is unqualified type-name if it's 178 // embedded. 179 func (field *goStructField) Selector() string { 180 if field.GoName != "" { 181 return field.GoName 182 } 183 // TODO(benkraft): This assumes the type is package-local, which is always 184 // true for embedded types for us, but isn't the most robust assumption. 185 return field.GoType.Unwrap().Reference() 186 } 187 188 // unmarshaler returns: 189 // - the name of the function to use to unmarshal this field 190 // - true if this is a fully-qualified name (false if it is a package-local 191 // unqualified name) 192 // - true if we need to generate an unmarshaler at all, false if the default 193 // behavior will suffice 194 func (field *goStructField) unmarshaler() (qualifiedName string, needsImport bool, needsUnmarshaler bool) { 195 switch typ := field.GoType.Unwrap().(type) { 196 case *goOpaqueType: 197 if typ.Unmarshaler != "" { 198 return typ.Unmarshaler, true, true 199 } 200 case *goInterfaceType: 201 return "__unmarshal" + typ.Reference(), false, true 202 } 203 return "encoding/json.Unmarshal", true, field.IsEmbedded() 204 } 205 206 // Unmarshaler returns the Go name of the function to use to unmarshal this 207 // field (which may be "json.Unmarshal" if there's not a special one). 208 func (field *goStructField) Unmarshaler(g *generator) (string, error) { 209 name, needsImport, _ := field.unmarshaler() 210 if needsImport { 211 return g.ref(name) 212 } 213 return name, nil 214 } 215 216 // marshaler returns: 217 // - the fully-qualified name of the function to use to marshal this field 218 // - true if we need to generate an marshaler at all, false if the default 219 // behavior will suffice 220 func (field *goStructField) marshaler() (qualifiedName string, needsImport bool, needsMarshaler bool) { 221 switch typ := field.GoType.Unwrap().(type) { 222 case *goOpaqueType: 223 if typ.Marshaler != "" { 224 return typ.Marshaler, true, true 225 } 226 case *goInterfaceType: 227 return "__marshal" + typ.Reference(), false, true 228 } 229 return "encoding/json.Marshal", true, field.IsEmbedded() 230 } 231 232 // Marshaler returns the Go name of the function to use to marshal this 233 // field (which may be "json.Marshal" if there's not a special one). 234 func (field *goStructField) Marshaler(g *generator) (string, error) { 235 name, needsImport, _ := field.marshaler() 236 if needsImport { 237 return g.ref(name) 238 } 239 return name, nil 240 } 241 242 // NeedsMarshaling returns true if this field needs special handling when 243 // marshaling and unmarshaling, e.g. if it has a user-specified custom 244 // (un)marshaler. Note if it needs one, it needs the other: even if the user 245 // only specified an unmarshaler, we need to add `json:"-"` to the field, which 246 // means we need to specially handling it when marshaling. 247 func (field *goStructField) NeedsMarshaling() bool { 248 _, _, ok1 := field.marshaler() 249 _, _, ok2 := field.unmarshaler() 250 return ok1 || ok2 251 } 252 253 // NeedsMarshaler returns true if any fields of this type need special 254 // handling when (un)marshaling (see goStructField.NeedsMarshaling). 255 func (typ *goStructType) NeedsMarshaling() bool { 256 for _, f := range typ.Fields { 257 if f.NeedsMarshaling() { 258 return true 259 } 260 } 261 return false 262 } 263 264 // selector represents a field and the path to get there from the type in 265 // question, and is used in FlattenedFields, below. 266 type selector struct { 267 *goStructField 268 // e.g. "OuterEmbed.InnerEmbed.LeafField" 269 Selector string 270 } 271 272 // FlattenedFields returns the fields of this type and its recursive embeds, 273 // and the paths to reach them (via those embeds), but with different 274 // visibility rules for conflicting fields than Go. 275 // 276 // (Before you read further, now's a good time to review Go's rules: 277 // https://golang.org/ref/spec#Selectors. Done? Good.) 278 // 279 // To illustrate the need, consider the following query: 280 // fragment A on T { id } 281 // fragment B on T { id } 282 // query Q { t { ...A ...B } } 283 // We generate types: 284 // type A struct { Id string `json:"id"` } 285 // type B struct { Id string `json:"id"` } 286 // type QT struct { A; B } 287 // According to Go's embedding rules, QT has no field Id: since QT.A.Id and 288 // QT.B.Id are at equal depth, neither wins and gets promoted. (Go's JSON 289 // library uses similar logic to decide which field to write to JSON, except 290 // with the additional rule that a field with a JSON tag wins over a field 291 // without; in our case both have such a field.) 292 // 293 // Those rules don't work for us. When unmarshaling, we want to fill in all 294 // the potentially-matching fields (QT.A.Id and QT.B.Id in this case), and when 295 // marshaling, we want to always marshal exactly one potentially-conflicting 296 // field; we're happy to use the Go visibility rules when they apply but we 297 // need to always marshal one field, even if there's not a clear best choice. 298 // For unmarshaling, our QT.UnmarshalJSON ends up unmarshaling the same JSON 299 // object into QT, QT.A, and QT.B, which gives us the behavior we want. But 300 // for marshaling, we need to resolve the conflicts: if we simply marshaled QT, 301 // QT.A, and QT.B, we'd have to do some JSON-surgery to join them, and we'd 302 // probably end up with duplicate fields, which leads to unpredictable behavior 303 // based on the reader. That's no good. 304 // 305 // So: instead, we have our own rules, which work like the Go rules, except 306 // that if there's a tie we choose the first field (in source order). (In 307 // practice, hopefully, they all match, but validating that is even more work 308 // for a fairly rare case.) This function returns, for each JSON-name, the Go 309 // field we want to use. In the example above, it would return: 310 // []selector{{<goStructField for QT.A.Id>, "A.Id"}} 311 func (typ *goStructType) FlattenedFields() ([]*selector, error) { 312 seenJSONNames := map[string]bool{} 313 retval := make([]*selector, 0, len(typ.Fields)) 314 315 queue := make([]*selector, len(typ.Fields)) 316 for i, field := range typ.Fields { 317 queue[i] = &selector{field, field.Selector()} 318 } 319 320 // Since our (non-embedded) fields always have JSON tags, the logic we want 321 // is simply: do a breadth-first search through the recursively embedded 322 // fields, and take the first one we see with a given JSON tag. 323 for len(queue) > 0 { 324 field := queue[0] 325 queue = queue[1:] 326 if field.IsEmbedded() { 327 typ, ok := field.GoType.(*goStructType) 328 if !ok { 329 // Should never happen: embeds correspond to named fragments, 330 // and even if the fragment is of interface type in GraphQL, 331 // either it's spread into a concrete type, or we are writing 332 // one of the implementations of the interface into which it's 333 // spread; either way we embed the corresponding implementation 334 // of the fragment. 335 return nil, errorf(nil, 336 "genqlient internal error: embedded field %s.%s was not a struct", 337 typ.GoName, field.GoName) 338 } 339 340 // Enqueue the embedded fields for our BFS. 341 for _, subField := range typ.Fields { 342 queue = append(queue, 343 &selector{subField, field.Selector + "." + subField.Selector()}) 344 } 345 continue 346 } 347 348 if seenJSONNames[field.JSONName] { 349 // We already chose a selector for this JSON field. Skip it. 350 continue 351 } 352 353 // Else, we are the selector we are looking for. 354 seenJSONNames[field.JSONName] = true 355 retval = append(retval, field) 356 } 357 return retval, nil 358 } 359 360 func (typ *goStructType) WriteDefinition(w io.Writer, g *generator) error { 361 writeDescription(w, structDescription(typ)) 362 363 fmt.Fprintf(w, "type %s struct {\n", typ.GoName) 364 for _, field := range typ.Fields { 365 writeDescription(w, field.Description) 366 jsonTag := `"` + field.JSONName 367 if field.Omitempty { 368 jsonTag += ",omitempty" 369 } 370 jsonTag += `"` 371 if field.NeedsMarshaling() { 372 // certain types are handled in our (Un)MarshalJSON (see below) 373 jsonTag = `"-"` 374 } 375 // Note for embedded types field.GoName is "", which produces the code 376 // we want! 377 fmt.Fprintf(w, "\t%s %s `json:%s`\n", 378 field.GoName, field.GoType.Reference(), jsonTag) 379 } 380 fmt.Fprintf(w, "}\n") 381 382 // Write out getter methods for each field. These are most useful for 383 // shared fields of an interface -- the methods will be included in the 384 // interface. But they can be useful in other cases, for example where you 385 // have a union several of whose members have a shared field (and can 386 // thereby be handled together). For simplicity's sake, we just write the 387 // methods always. 388 // 389 // Note we use the *flattened* fields here, which ensures we avoid 390 // conflicts in the case where multiple embedded types include the same 391 // field. 392 flattened, err := typ.FlattenedFields() 393 if err != nil { 394 return err 395 } 396 for _, field := range flattened { 397 description := fmt.Sprintf( 398 "Get%s returns %s.%s, and is useful for accessing the field via an interface.", 399 field.GoName, typ.GoName, field.GoName) 400 writeDescription(w, description) 401 fmt.Fprintf(w, "func (v *%s) Get%s() %s { return v.%s }\n", 402 typ.GoName, field.GoName, field.GoType.Reference(), field.Selector) 403 } 404 405 // Now, if needed, write the marshaler/unmarshaler. We need one if we have 406 // any interface-typed fields, or any embedded fields. 407 // 408 // For interface-typed fields, ideally we'd write an UnmarshalJSON method 409 // on the field, but you can't add a method to an interface. So we write a 410 // per-interface-type helper, but we have to call it (with a little 411 // boilerplate) everywhere the type is referenced. 412 // 413 // For embedded fields (from fragments), mostly the JSON library would just 414 // do what we want, but there are two problems. First, if the embedded 415 // type has its own UnmarshalJSON, naively that would be promoted to 416 // become our UnmarshalJSON, which is no good. But we don't want to just 417 // hide that method and inline its fields, either; we need to call its 418 // UnmarshalJSON (on the same object we unmarshal into this struct). 419 // Second, if the embedded type duplicates any fields of the embedding type 420 // -- maybe both the fragment and the selection into which it's spread 421 // select the same field, or several fragments select the same field -- the 422 // JSON library will only fill one of those (the least-nested one); we want 423 // to fill them all. 424 // 425 // For fields with a custom marshaler or unmarshaler, we do basically the 426 // same thing as interface-typed fields, except the user has defined the 427 // helper. 428 // 429 // Note that genqlient itself only uses unmarshalers for output types, and 430 // marshalers for input types. But we write both in case you want to write 431 // your data to JSON for some reason (say to put it in a cache). (And we 432 // need to write both if we need to write either, because in such cases we 433 // write a `json:"-"` tag on the field.) 434 // 435 // TODO(benkraft): If/when proposal #5901 is implemented (Go 1.18 at the 436 // earliest), we may be able to do some of this a simpler way. 437 if typ.NeedsMarshaling() { 438 err := g.render("unmarshal.go.tmpl", w, typ) 439 if err != nil { 440 return err 441 } 442 err = g.render("marshal.go.tmpl", w, typ) 443 if err != nil { 444 return err 445 } 446 } 447 return nil 448 } 449 450 func (typ *goStructType) Reference() string { return typ.GoName } 451 func (typ *goStructType) SelectionSet() ast.SelectionSet { return typ.Selection } 452 func (typ *goStructType) GraphQLTypeName() string { return typ.GraphQLName } 453 454 // goInterfaceType represents a Go interface type, used to represent a GraphQL 455 // interface or union type. 456 type goInterfaceType struct { 457 GoName string 458 // Fields shared by all the interface's implementations; 459 // we'll generate getter methods for each. 460 SharedFields []*goStructField 461 Implementations []*goStructType 462 Selection ast.SelectionSet 463 descriptionInfo 464 } 465 466 func (typ *goInterfaceType) WriteDefinition(w io.Writer, g *generator) error { 467 writeDescription(w, interfaceDescription(typ)) 468 469 // Write the interface. 470 fmt.Fprintf(w, "type %s interface {\n", typ.GoName) 471 implementsMethodName := fmt.Sprintf("implementsGraphQLInterface%v", typ.GoName) 472 fmt.Fprintf(w, "\t%s()\n", implementsMethodName) 473 for _, sharedField := range typ.SharedFields { 474 if sharedField.GoName == "" { // embedded type 475 fmt.Fprintf(w, "\t%s\n", sharedField.GoType.Reference()) 476 continue 477 } 478 479 methodName := "Get" + sharedField.GoName 480 description := "" 481 if sharedField.GraphQLName == "__typename" { 482 description = fmt.Sprintf( 483 "%s returns the receiver's concrete GraphQL type-name "+ 484 "(see interface doc for possible values).", methodName) 485 } else { 486 description = fmt.Sprintf( 487 `%s returns the interface-field %q from its implementation.`, 488 methodName, sharedField.GraphQLName) 489 if sharedField.Description != "" { 490 description = fmt.Sprintf( 491 "%s\nThe GraphQL interface field's documentation follows.\n\n%s", 492 description, sharedField.Description) 493 } 494 } 495 496 writeDescription(w, description) 497 fmt.Fprintf(w, "\t%s() %s\n", methodName, sharedField.GoType.Reference()) 498 } 499 500 var name string 501 502 if typ.FragmentName != "" { 503 name = typ.FragmentName 504 } else { 505 name = typ.GraphQLName 506 } 507 508 for _, impl := range typ.Implementations { 509 fmt.Fprintf(w, "\tGet%s%s() (*%s, bool)\n", name, impl.GraphQLName, 510 impl.Reference()) 511 } 512 513 fmt.Fprintf(w, "}\n") 514 515 // Now, write out the implementations. 516 for _, impl := range typ.Implementations { 517 fmt.Fprintf(w, "func (v *%s) %s() {}\n", 518 impl.Reference(), implementsMethodName) 519 520 for _, targetImpl := range typ.Implementations { 521 var code string 522 523 if impl.GoName == targetImpl.GoName { 524 code = "return v, true" 525 } else { 526 code = "return nil, false" 527 } 528 529 fmt.Fprintf(w, "func (v *%s) Get%s%s() (*%s, bool) {\n\t%s\n}\n", 530 impl.Reference(), name, targetImpl.GraphQLName, 531 targetImpl.Reference(), code) 532 } 533 } 534 535 // Finally, write the marshal- and unmarshal-helpers, which 536 // will be called by struct fields referencing this type (see 537 // goStructType.WriteDefinition). 538 err := g.render("unmarshal_helper.go.tmpl", w, typ) 539 if err != nil { 540 return err 541 } 542 return g.render("marshal_helper.go.tmpl", w, typ) 543 } 544 545 func (typ *goInterfaceType) Reference() string { return typ.GoName } 546 func (typ *goInterfaceType) SelectionSet() ast.SelectionSet { return typ.Selection } 547 func (typ *goInterfaceType) GraphQLTypeName() string { return typ.GraphQLName } 548 549 func (typ *goOpaqueType) Unwrap() goType { return typ } 550 func (typ *goTypenameForBuiltinType) Unwrap() goType { return typ } 551 func (typ *goSliceType) Unwrap() goType { return typ.Elem.Unwrap() } 552 func (typ *goPointerType) Unwrap() goType { return typ.Elem.Unwrap() } 553 func (typ *goEnumType) Unwrap() goType { return typ } 554 func (typ *goStructType) Unwrap() goType { return typ } 555 func (typ *goInterfaceType) Unwrap() goType { return typ } 556 557 func (typ *goOpaqueType) SliceDepth() int { return 0 } 558 func (typ *goTypenameForBuiltinType) SliceDepth() int { return 0 } 559 func (typ *goSliceType) SliceDepth() int { return typ.Elem.SliceDepth() + 1 } 560 func (typ *goPointerType) SliceDepth() int { return 0 } 561 func (typ *goEnumType) SliceDepth() int { return 0 } 562 func (typ *goStructType) SliceDepth() int { return 0 } 563 func (typ *goInterfaceType) SliceDepth() int { return 0 } 564 565 func (typ *goOpaqueType) IsPointer() bool { return false } 566 func (typ *goTypenameForBuiltinType) IsPointer() bool { return false } 567 func (typ *goSliceType) IsPointer() bool { return typ.Elem.IsPointer() } 568 func (typ *goPointerType) IsPointer() bool { return true } 569 func (typ *goEnumType) IsPointer() bool { return false } 570 func (typ *goStructType) IsPointer() bool { return false } 571 func (typ *goInterfaceType) IsPointer() bool { return false } 572 573 func writeDescription(w io.Writer, desc string) { 574 if desc != "" { 575 for _, line := range strings.Split(desc, "\n") { 576 fmt.Fprintf(w, "// %s\n", strings.TrimLeft(line, " \t")) 577 } 578 } 579 }