1package codegen
2
3import (
4 "go/types"
5 "strconv"
6 "strings"
7 "unicode"
8
9 "github.com/99designs/gqlgen/codegen/config"
10 "github.com/pkg/errors"
11 "github.com/vektah/gqlparser/ast"
12)
13
14type GoFieldType int
15
16const (
17 GoFieldUndefined GoFieldType = iota
18 GoFieldMethod
19 GoFieldVariable
20 GoFieldMap
21)
22
23type Object struct {
24 *ast.Definition
25
26 Type types.Type
27 ResolverInterface types.Type
28 Root bool
29 Fields []*Field
30 Implements []*ast.Definition
31 DisableConcurrency bool
32 Stream bool
33 Directives []*Directive
34}
35
36func (b *builder) buildObject(typ *ast.Definition) (*Object, error) {
37 dirs, err := b.getDirectives(typ.Directives)
38 if err != nil {
39 return nil, errors.Wrap(err, typ.Name)
40 }
41
42 obj := &Object{
43 Definition: typ,
44 Root: b.Schema.Query == typ || b.Schema.Mutation == typ || b.Schema.Subscription == typ,
45 DisableConcurrency: typ == b.Schema.Mutation,
46 Stream: typ == b.Schema.Subscription,
47 Directives: dirs,
48 ResolverInterface: types.NewNamed(
49 types.NewTypeName(0, b.Config.Exec.Pkg(), typ.Name+"Resolver", nil),
50 nil,
51 nil,
52 ),
53 }
54
55 if !obj.Root {
56 goObject, err := b.Binder.DefaultUserObject(typ.Name)
57 if err != nil {
58 return nil, err
59 }
60 obj.Type = goObject
61 }
62
63 for _, intf := range b.Schema.GetImplements(typ) {
64 obj.Implements = append(obj.Implements, b.Schema.Types[intf.Name])
65 }
66
67 for _, field := range typ.Fields {
68 if strings.HasPrefix(field.Name, "__") {
69 continue
70 }
71
72 var f *Field
73 f, err = b.buildField(obj, field)
74 if err != nil {
75 return nil, err
76 }
77
78 obj.Fields = append(obj.Fields, f)
79 }
80
81 return obj, nil
82}
83
84func (o *Object) Reference() types.Type {
85 switch o.Type.(type) {
86 case *types.Pointer, *types.Slice, *types.Map:
87 return o.Type
88 }
89
90 return types.NewPointer(o.Type)
91}
92
93type Objects []*Object
94
95func (o *Object) Implementors() string {
96 satisfiedBy := strconv.Quote(o.Name)
97 for _, s := range o.Implements {
98 satisfiedBy += ", " + strconv.Quote(s.Name)
99 }
100 return "[]string{" + satisfiedBy + "}"
101}
102
103func (o *Object) HasResolvers() bool {
104 for _, f := range o.Fields {
105 if f.IsResolver {
106 return true
107 }
108 }
109 return false
110}
111
112func (o *Object) HasUnmarshal() bool {
113 if o.Type == config.MapType {
114 return true
115 }
116 for i := 0; i < o.Type.(*types.Named).NumMethods(); i++ {
117 switch o.Type.(*types.Named).Method(i).Name() {
118 case "UnmarshalGQL":
119 return true
120 }
121 }
122 return false
123}
124
125func (o *Object) HasDirectives() bool {
126 if len(o.Directives) > 0 {
127 return true
128 }
129 for _, f := range o.Fields {
130 if f.HasDirectives() {
131 return true
132 }
133 }
134
135 return false
136}
137
138func (o *Object) IsConcurrent() bool {
139 for _, f := range o.Fields {
140 if f.IsConcurrent() {
141 return true
142 }
143 }
144 return false
145}
146
147func (o *Object) IsReserved() bool {
148 return strings.HasPrefix(o.Definition.Name, "__")
149}
150
151func (o *Object) Description() string {
152 return o.Definition.Description
153}
154
155func (os Objects) ByName(name string) *Object {
156 for i, o := range os {
157 if strings.EqualFold(o.Definition.Name, name) {
158 return os[i]
159 }
160 }
161 return nil
162}
163
164func ucFirst(s string) string {
165 if s == "" {
166 return ""
167 }
168
169 r := []rune(s)
170 r[0] = unicode.ToUpper(r[0])
171 return string(r)
172}