1 package graphql
2
3 import (
4 "bytes"
5 "encoding/json"
6 "io"
7 "reflect"
8 "sort"
9
10 "github.com/cli/shurcooL-graphql/ident"
11 )
12
13 func constructQuery(v any, variables map[string]any, queryName string) string {
14 query := query(v)
15 if len(variables) > 0 {
16 return "query" + queryNameFormat(queryName) + "(" + queryArguments(variables) + ")" + query
17 } else if queryName != "" {
18 return "query" + queryNameFormat(queryName) + query
19 }
20 return query
21 }
22
23 func constructMutation(v any, variables map[string]any, queryName string) string {
24 query := query(v)
25 if len(variables) > 0 {
26 return "mutation" + queryNameFormat(queryName) + "(" + queryArguments(variables) + ")" + query
27 }
28 return "mutation" + queryNameFormat(queryName) + query
29 }
30
31 func queryNameFormat(n string) string {
32 if n != "" {
33 return " " + n
34 }
35 return n
36 }
37
38
39
40
41 func queryArguments(variables map[string]any) string {
42
43
44 keys := make([]string, 0, len(variables))
45 for k := range variables {
46 keys = append(keys, k)
47 }
48 sort.Strings(keys)
49
50 var buf bytes.Buffer
51 for _, k := range keys {
52 _, _ = io.WriteString(&buf, "$")
53 _, _ = io.WriteString(&buf, k)
54 _, _ = io.WriteString(&buf, ":")
55 writeArgumentType(&buf, reflect.TypeOf(variables[k]), true)
56
57
58
59 }
60 return buf.String()
61 }
62
63
64
65
66 func writeArgumentType(w io.Writer, t reflect.Type, value bool) {
67 if t.Kind() == reflect.Ptr {
68
69 writeArgumentType(w, t.Elem(), false)
70 return
71 }
72
73 switch t.Kind() {
74 case reflect.Slice, reflect.Array:
75
76 _, _ = io.WriteString(w, "[")
77 writeArgumentType(w, t.Elem(), true)
78 _, _ = io.WriteString(w, "]")
79 default:
80
81 name := t.Name()
82 if name == "string" {
83 name = "ID"
84 }
85 _, _ = io.WriteString(w, name)
86 }
87
88 if value {
89
90 _, _ = io.WriteString(w, "!")
91 }
92 }
93
94
95
96
97
98 func query(v any) string {
99 var buf bytes.Buffer
100 writeQuery(&buf, reflect.TypeOf(v), false)
101 return buf.String()
102 }
103
104
105
106 func writeQuery(w io.Writer, t reflect.Type, inline bool) {
107 switch t.Kind() {
108 case reflect.Ptr, reflect.Slice:
109 writeQuery(w, t.Elem(), false)
110 case reflect.Struct:
111
112 if reflect.PtrTo(t).Implements(jsonUnmarshaler) {
113 return
114 }
115 if !inline {
116 _, _ = io.WriteString(w, "{")
117 }
118 for i := 0; i < t.NumField(); i++ {
119 if i != 0 {
120 _, _ = io.WriteString(w, ",")
121 }
122 f := t.Field(i)
123 value, ok := f.Tag.Lookup("graphql")
124 inlineField := f.Anonymous && !ok
125 if !inlineField {
126 if ok {
127 _, _ = io.WriteString(w, value)
128 } else {
129 _, _ = io.WriteString(w, ident.ParseMixedCaps(f.Name).ToLowerCamelCase())
130 }
131 }
132 writeQuery(w, f.Type, inlineField)
133 }
134 if !inline {
135 _, _ = io.WriteString(w, "}")
136 }
137 }
138 }
139
140 var jsonUnmarshaler = reflect.TypeOf((*json.Unmarshaler)(nil)).Elem()
141
View as plain text