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