Skip to content

Commit d9ce49d

Browse files
authored
Propagate errors instead of panic (#41)
* Propagate errors instead of panic
1 parent 7e9cc29 commit d9ce49d

File tree

2 files changed

+47
-18
lines changed

2 files changed

+47
-18
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
.idea/

query.go

+46-18
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,10 @@ func constructOptions(options []Option) (*constructOptionsOutput, error) {
4545

4646
// ConstructQuery build GraphQL query string from struct and variables
4747
func ConstructQuery(v interface{}, variables map[string]interface{}, options ...Option) (string, error) {
48-
query := query(v)
48+
query, err := query(v)
49+
if err != nil {
50+
return "", err
51+
}
4952

5053
optionsOutput, err := constructOptions(options)
5154
if err != nil {
@@ -65,7 +68,10 @@ func ConstructQuery(v interface{}, variables map[string]interface{}, options ...
6568

6669
// ConstructQuery build GraphQL mutation string from struct and variables
6770
func ConstructMutation(v interface{}, variables map[string]interface{}, options ...Option) (string, error) {
68-
query := query(v)
71+
query, err := query(v)
72+
if err != nil {
73+
return "", err
74+
}
6975
optionsOutput, err := constructOptions(options)
7076
if err != nil {
7177
return "", err
@@ -83,7 +89,10 @@ func ConstructMutation(v interface{}, variables map[string]interface{}, options
8389

8490
// ConstructSubscription build GraphQL subscription string from struct and variables
8591
func ConstructSubscription(v interface{}, variables map[string]interface{}, options ...Option) (string, error) {
86-
query := query(v)
92+
query, err := query(v)
93+
if err != nil {
94+
return "", err
95+
}
8796
optionsOutput, err := constructOptions(options)
8897
if err != nil {
8998
return "", err
@@ -171,22 +180,28 @@ func writeArgumentType(w io.Writer, t reflect.Type, value bool) {
171180
// a minified query string from the provided struct v.
172181
//
173182
// E.g., struct{Foo Int, BarBaz *Boolean} -> "{foo,barBaz}".
174-
func query(v interface{}) string {
183+
func query(v interface{}) (string, error) {
175184
var buf bytes.Buffer
176-
writeQuery(&buf, reflect.TypeOf(v), reflect.ValueOf(v), false)
177-
return buf.String()
185+
err := writeQuery(&buf, reflect.TypeOf(v), reflect.ValueOf(v), false)
186+
if err != nil {
187+
return "", fmt.Errorf("failed to write query: %w", err)
188+
}
189+
return buf.String(), nil
178190
}
179191

180192
// writeQuery writes a minified query for t to w.
181193
// If inline is true, the struct fields of t are inlined into parent struct.
182-
func writeQuery(w io.Writer, t reflect.Type, v reflect.Value, inline bool) {
194+
func writeQuery(w io.Writer, t reflect.Type, v reflect.Value, inline bool) error {
183195
switch t.Kind() {
184196
case reflect.Ptr:
185-
writeQuery(w, t.Elem(), ElemSafe(v), false)
197+
err := writeQuery(w, t.Elem(), ElemSafe(v), false)
198+
if err != nil {
199+
return fmt.Errorf("failed to write query for ptr `%v`: %w", t, err)
200+
}
186201
case reflect.Struct:
187202
// If the type implements json.Unmarshaler, it's a scalar. Don't expand it.
188203
if reflect.PtrTo(t).Implements(jsonUnmarshaler) {
189-
return
204+
return nil
190205
}
191206
if !inline {
192207
io.WriteString(w, "{")
@@ -216,20 +231,25 @@ func writeQuery(w io.Writer, t reflect.Type, v reflect.Value, inline bool) {
216231
if isTrue(f.Tag.Get("scalar")) {
217232
continue
218233
}
219-
writeQuery(w, f.Type, FieldSafe(v, i), inlineField)
234+
err := writeQuery(w, f.Type, FieldSafe(v, i), inlineField)
235+
if err != nil {
236+
return fmt.Errorf("failed to write query for struct field `%v`: %w", f.Name, err)
237+
}
220238
}
221239
if !inline {
222240
io.WriteString(w, "}")
223241
}
224242
case reflect.Slice:
225243
if t.Elem().Kind() != reflect.Array {
226-
writeQuery(w, t.Elem(), IndexSafe(v, 0), false)
227-
return
244+
err := writeQuery(w, t.Elem(), IndexSafe(v, 0), false)
245+
if err != nil {
246+
return fmt.Errorf("failed to write query for slice item `%v`: %w", t, err)
247+
}
248+
return nil
228249
}
229250
// handle [][2]interface{} like an ordered map
230251
if t.Elem().Len() != 2 {
231-
err := fmt.Errorf("only arrays of len 2 are supported, got %v", t.Elem())
232-
panic(err.Error())
252+
return fmt.Errorf("only arrays of len 2 are supported, got %v", t.Elem())
233253
}
234254
sliceOfPairs := v
235255
_, _ = io.WriteString(w, "{")
@@ -238,14 +258,22 @@ func writeQuery(w io.Writer, t reflect.Type, v reflect.Value, inline bool) {
238258
// it.Value() returns interface{}, so we need to use reflect.ValueOf
239259
// to cast it away
240260
key, val := pair.Index(0), reflect.ValueOf(pair.Index(1).Interface())
241-
_, _ = io.WriteString(w, key.Interface().(string))
242-
writeQuery(w, val.Type(), val, false)
261+
keyString, ok := key.Interface().(string)
262+
if !ok {
263+
return fmt.Errorf("expected pair (string, %v), got (%v, %v)",
264+
val.Type(), key.Type(), val.Type())
265+
}
266+
_, _ = io.WriteString(w, keyString)
267+
err := writeQuery(w, val.Type(), val, false)
268+
if err != nil {
269+
return fmt.Errorf("failed to write query for pair[1] `%v`: %w", val.Type(), err)
270+
}
243271
}
244272
_, _ = io.WriteString(w, "}")
245273
case reflect.Map:
246-
err := fmt.Errorf("type %v is not supported, use [][2]interface{} instead", t)
247-
panic(err.Error())
274+
return fmt.Errorf("type %v is not supported, use [][2]interface{} instead", t)
248275
}
276+
return nil
249277
}
250278

251279
func IndexSafe(v reflect.Value, i int) reflect.Value {

0 commit comments

Comments
 (0)