util.go

  1package graphql
  2
  3import (
  4	"fmt"
  5	"reflect"
  6	"strings"
  7)
  8
  9const TAG = "json"
 10
 11// can't take recursive slice type
 12// e.g
 13// type Person struct{
 14//	Friends []Person
 15// }
 16// it will throw panic stack-overflow
 17func BindFields(obj interface{}) Fields {
 18	v := reflect.ValueOf(obj)
 19	fields := make(map[string]*Field)
 20
 21	for i := 0; i < v.NumField(); i++ {
 22		typeField := v.Type().Field(i)
 23
 24		tag := extractTag(typeField.Tag)
 25		if tag == "-" {
 26			continue
 27		}
 28
 29		var graphType Output
 30		if typeField.Type.Kind() == reflect.Struct {
 31
 32			structFields := BindFields(v.Field(i).Interface())
 33			if tag == "" {
 34				fields = appendFields(fields, structFields)
 35				continue
 36			} else {
 37				graphType = NewObject(ObjectConfig{
 38					Name:   tag,
 39					Fields: structFields,
 40				})
 41			}
 42		}
 43
 44		if tag == "" {
 45			continue
 46		}
 47
 48		if graphType == nil {
 49			graphType = getGraphType(typeField.Type)
 50		}
 51		fields[tag] = &Field{
 52			Type: graphType,
 53			Resolve: func(p ResolveParams) (interface{}, error) {
 54				return extractValue(tag, p.Source), nil
 55			},
 56		}
 57	}
 58	return fields
 59}
 60
 61func getGraphType(tipe reflect.Type) Output {
 62	kind := tipe.Kind()
 63	switch kind {
 64	case reflect.String:
 65		return String
 66	case reflect.Int:
 67		fallthrough
 68	case reflect.Int8:
 69		fallthrough
 70	case reflect.Int32:
 71		fallthrough
 72	case reflect.Int64:
 73		return Int
 74	case reflect.Float32:
 75		fallthrough
 76	case reflect.Float64:
 77		return Float
 78	case reflect.Bool:
 79		return Boolean
 80	case reflect.Slice:
 81		return getGraphList(tipe)
 82	}
 83	return String
 84}
 85
 86func getGraphList(tipe reflect.Type) *List {
 87	if tipe.Kind() == reflect.Slice {
 88		switch tipe.Elem().Kind() {
 89		case reflect.Int:
 90			fallthrough
 91		case reflect.Int8:
 92			fallthrough
 93		case reflect.Int32:
 94			fallthrough
 95		case reflect.Int64:
 96			return NewList(Int)
 97		case reflect.Bool:
 98			return NewList(Boolean)
 99		case reflect.Float32:
100			fallthrough
101		case reflect.Float64:
102			return NewList(Float)
103		case reflect.String:
104			return NewList(String)
105		}
106	}
107	// finaly bind object
108	t := reflect.New(tipe.Elem())
109	name := strings.Replace(fmt.Sprint(tipe.Elem()), ".", "_", -1)
110	obj := NewObject(ObjectConfig{
111		Name:   name,
112		Fields: BindFields(t.Elem().Interface()),
113	})
114	return NewList(obj)
115}
116
117func appendFields(dest, origin Fields) Fields {
118	for key, value := range origin {
119		dest[key] = value
120	}
121	return dest
122}
123
124func extractValue(originTag string, obj interface{}) interface{} {
125	val := reflect.ValueOf(obj)
126
127	for j := 0; j < val.NumField(); j++ {
128		typeField := val.Type().Field(j)
129		if typeField.Type.Kind() == reflect.Struct {
130			res := extractValue(originTag, val.Field(j).Interface())
131			if res != nil {
132				return res
133			}
134		}
135
136		if originTag == extractTag(typeField.Tag) {
137			return val.Field(j).Interface()
138		}
139	}
140	return nil
141}
142
143func extractTag(tag reflect.StructTag) string {
144	t := tag.Get(TAG)
145	if t != "" {
146		t = strings.Split(t, ",")[0]
147	}
148	return t
149}
150
151// lazy way of binding args
152func BindArg(obj interface{}, tags ...string) FieldConfigArgument {
153	v := reflect.ValueOf(obj)
154	var config = make(FieldConfigArgument)
155	for i := 0; i < v.NumField(); i++ {
156		typeField := v.Type().Field(i)
157
158		mytag := extractTag(typeField.Tag)
159		if inArray(tags, mytag) {
160			config[mytag] = &ArgumentConfig{
161				Type: getGraphType(typeField.Type),
162			}
163		}
164	}
165	return config
166}
167
168func inArray(slice interface{}, item interface{}) bool {
169	s := reflect.ValueOf(slice)
170	if s.Kind() != reflect.Slice {
171		panic("inArray() given a non-slice type")
172	}
173
174	for i := 0; i < s.Len(); i++ {
175		if reflect.DeepEqual(item, s.Index(i).Interface()) {
176			return true
177		}
178	}
179	return false
180}