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 if o.Type.(*types.Named).Method(i).Name() == "UnmarshalGQL" {
118 return true
119 }
120 }
121 return false
122}
123
124func (o *Object) HasDirectives() bool {
125 if len(o.Directives) > 0 {
126 return true
127 }
128 for _, f := range o.Fields {
129 if f.HasDirectives() {
130 return true
131 }
132 }
133
134 return false
135}
136
137func (o *Object) IsConcurrent() bool {
138 for _, f := range o.Fields {
139 if f.IsConcurrent() {
140 return true
141 }
142 }
143 return false
144}
145
146func (o *Object) IsReserved() bool {
147 return strings.HasPrefix(o.Definition.Name, "__")
148}
149
150func (o *Object) Description() string {
151 return o.Definition.Description
152}
153
154func (os Objects) ByName(name string) *Object {
155 for i, o := range os {
156 if strings.EqualFold(o.Definition.Name, name) {
157 return os[i]
158 }
159 }
160 return nil
161}
162
163func ucFirst(s string) string {
164 if s == "" {
165 return ""
166 }
167
168 r := []rune(s)
169 r[0] = unicode.ToUpper(r[0])
170 return string(r)
171}