github.com/hairyhenderson/gomplate/v4@v4.0.0-pre-2.0.20240520121557-362f058f0c93/coll/jq.go (about) 1 package coll 2 3 import ( 4 "context" 5 "encoding/json" 6 "fmt" 7 "reflect" 8 9 "github.com/itchyny/gojq" 10 ) 11 12 // JQ - 13 func JQ(ctx context.Context, jqExpr string, in interface{}) (interface{}, error) { 14 query, err := gojq.Parse(jqExpr) 15 if err != nil { 16 return nil, fmt.Errorf("jq parsing expression %q: %w", jqExpr, err) 17 } 18 19 // convert input to a supported type, if necessary 20 in, err = jqConvertType(in) 21 if err != nil { 22 return nil, fmt.Errorf("jq type conversion: %w", err) 23 } 24 25 iter := query.RunWithContext(ctx, in) 26 var out interface{} 27 a := []interface{}{} 28 for { 29 v, ok := iter.Next() 30 if !ok { 31 break 32 } 33 if err, ok := v.(error); ok { 34 return nil, fmt.Errorf("jq execution: %w", err) 35 } 36 a = append(a, v) 37 } 38 if len(a) == 1 { 39 out = a[0] 40 } else { 41 out = a 42 } 43 44 return out, nil 45 } 46 47 // jqConvertType converts the input to a map[string]interface{}, []interface{}, 48 // or other supported primitive JSON types. 49 func jqConvertType(in interface{}) (interface{}, error) { 50 // if it's already a supported type, pass it through 51 switch in.(type) { 52 case map[string]interface{}, []interface{}, 53 string, []byte, 54 nil, bool, 55 int, int8, int16, int32, int64, 56 uint, uint8, uint16, uint32, uint64, 57 float32, float64: 58 return in, nil 59 } 60 61 inType := reflect.TypeOf(in) 62 value := reflect.ValueOf(in) 63 64 // pointers need to be dereferenced first 65 if inType.Kind() == reflect.Ptr { 66 inType = inType.Elem() 67 value = value.Elem() 68 } 69 70 mapType := reflect.TypeOf(map[string]interface{}{}) 71 sliceType := reflect.TypeOf([]interface{}{}) 72 // if it can be converted to a map or slice, do that 73 if inType.ConvertibleTo(mapType) { 74 return value.Convert(mapType).Interface(), nil 75 } else if inType.ConvertibleTo(sliceType) { 76 return value.Convert(sliceType).Interface(), nil 77 } 78 79 // if it's a struct, the simplest (though not necessarily most efficient) 80 // is to JSON marshal/unmarshal it 81 if inType.Kind() == reflect.Struct { 82 b, err := json.Marshal(in) 83 if err != nil { 84 return nil, fmt.Errorf("json marshal struct: %w", err) 85 } 86 var m map[string]interface{} 87 err = json.Unmarshal(b, &m) 88 if err != nil { 89 return nil, fmt.Errorf("json unmarshal struct: %w", err) 90 } 91 return m, nil 92 } 93 94 // we maybe don't need to convert the value, so return it as-is 95 return in, nil 96 }