github.com/dbernstein1/tyk@v2.9.0-beta9-dl-apic+incompatible/gateway/jq.go (about) 1 // +build jq 2 3 package gateway 4 5 // #cgo LDFLAGS: -ljq 6 // #include <jq.h> 7 // #include <jv.h> 8 import "C" 9 import ( 10 "errors" 11 "fmt" 12 "reflect" 13 ) 14 15 // JQ type stores a JQ vm 16 // Must be protected with mutex in threaded environment 17 type JQ struct { 18 state *C.jq_state 19 } 20 21 // NewJQ creates a bew JQ vm. program is the actual JQ filter to compile 22 func NewJQ(program string) (*JQ, error) { 23 state := C.jq_init() 24 25 if rc := C.jq_compile(state, C.CString(program)); rc == 0 { 26 C.jq_teardown(&state) 27 return nil, errors.New("Unable to compile jq filter") 28 } 29 30 return &JQ{state}, nil 31 } 32 33 // Handle applies the compiled filter ton the value 34 func (jq *JQ) Handle(value interface{}) (interface{}, error) { 35 jv := goToJv(value) 36 if !isValid(jv) { 37 C.jv_free(jv) 38 return nil, errors.New("Invalid JSON") 39 } 40 41 C.jq_start(jq.state, jv, 0) 42 jvResult := C.jq_next(jq.state) 43 44 if !isValid(jvResult) { 45 C.jv_free(jvResult) 46 return nil, errors.New("Error while applying JQ transformation XXX: Get the error message from jv_result") 47 } 48 49 result := jvToGo(jvResult) 50 C.jv_free(jvResult) 51 return result, nil 52 } 53 54 func isValid(jv C.jv) bool { 55 return C.jv_is_valid(jv) != 0 56 } 57 58 func goToJv(v interface{}) C.jv { 59 if v == nil { 60 return C.jv_null() 61 } 62 63 value := reflect.Indirect(reflect.ValueOf(v)) 64 65 switch value.Type().Kind() { 66 case reflect.Bool: 67 if value.Bool() { 68 return C.jv_true() 69 } else { 70 return C.jv_false() 71 } 72 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 73 return C.jv_number(C.double(value.Int())) 74 // TODO reflect.Uintptr? 75 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: 76 return C.jv_number(C.double(value.Uint())) 77 case reflect.Float32, reflect.Float64: 78 return C.jv_number(C.double(value.Float())) 79 case reflect.String: 80 return C.jv_string(C.CString(value.String())) 81 case reflect.Array, reflect.Slice: 82 n := value.Len() 83 arr := C.jv_array_sized(C.int(n)) 84 for i := 0; i < n; i++ { 85 item := goToJv(value.Index(i).Interface()) 86 arr = C.jv_array_set(C.jv_copy(arr), C.int(i), item) 87 } 88 return arr 89 case reflect.Map: 90 // TODO assert key is string? 91 object := C.jv_object() 92 for _, k := range value.MapKeys() { 93 key := goToJv(k.Interface()) 94 mapValue := goToJv(value.MapIndex(k).Interface()) 95 object = C.jv_object_set(object, key, mapValue) 96 } 97 return object 98 } 99 100 msg := fmt.Sprintf("unknown type for: %v", value.Interface()) 101 102 return C.jv_invalid_with_msg(C.jv_string(C.CString(msg))) 103 } 104 105 func jvToGo(value C.jv) interface{} { 106 switch C.jv_get_kind(value) { 107 case C.JV_KIND_INVALID: 108 return errors.New("invalid") 109 case C.JV_KIND_NULL: 110 return nil 111 case C.JV_KIND_FALSE: 112 return false 113 case C.JV_KIND_TRUE: 114 return true 115 case C.JV_KIND_NUMBER: 116 number := C.jv_number_value(value) 117 if C.jv_is_integer(value) == 0 { 118 return float64(number) 119 } else { 120 return int(number) 121 } 122 case C.JV_KIND_STRING: 123 return C.GoString(C.jv_string_value(value)) 124 case C.JV_KIND_ARRAY: 125 length := C.jv_array_length(C.jv_copy(value)) 126 arr := make([]interface{}, length) 127 for i := range arr { 128 arr[i] = jvToGo(C.jv_array_get(C.jv_copy(value), C.int(i))) 129 } 130 return arr 131 case C.JV_KIND_OBJECT: 132 result := make(map[string]interface{}) 133 var k, v C.jv 134 for jv_i := C.jv_object_iter(value); C.jv_object_iter_valid(value, jv_i) != 0; jv_i = C.jv_object_iter_next(value, jv_i) { 135 k = C.jv_object_iter_key(value, jv_i) 136 v = C.jv_object_iter_value(value, jv_i) 137 result[C.GoString(C.jv_string_value(k))] = jvToGo(v) 138 } 139 return result 140 default: 141 return errors.New("unknown type") 142 } 143 }