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  }