github.com/treeverse/lakefs@v1.24.1-0.20240520134607-95648127bfb0/pkg/actions/lua/util/deep_push.go (about) 1 package util 2 3 import ( 4 "reflect" 5 6 "github.com/Shopify/go-lua" 7 ) 8 9 // DeepPush will put any basic Go type on the lua stack. If the value 10 // contains a map or a slice, it will recursively push those values as 11 // tables on the Lua stack. 12 // 13 // Supported types are: 14 // 15 // | Go | Lua 16 // |------------------------------------------------------------------------- 17 // | nil | nil 18 // | bool | bool 19 // | string | string 20 // | any int | number (float64) 21 // | any float | number (float64) 22 // | any complex | number (real value as float64) 23 // | | 24 // | map[t]t | table, key and val `t` recursively 25 // | | resolved 26 // | | 27 // | []t | table with array properties, with `t` 28 // | | values recursively resolved 29 func DeepPush(l *lua.State, v interface{}) int { 30 forwardOnType(l, v) 31 return 1 32 } 33 34 func forwardOnType(l *lua.State, val interface{}) { 35 switch val := val.(type) { 36 case nil: 37 l.PushNil() 38 39 case bool: 40 l.PushBoolean(val) 41 42 case string: 43 l.PushString(val) 44 45 case uint8: 46 l.PushNumber(float64(val)) 47 case uint16: 48 l.PushNumber(float64(val)) 49 case uint32: 50 l.PushNumber(float64(val)) 51 case uint64: 52 l.PushNumber(float64(val)) 53 case uint: 54 l.PushNumber(float64(val)) 55 56 case int8: 57 l.PushNumber(float64(val)) 58 case int16: 59 l.PushNumber(float64(val)) 60 case int32: 61 l.PushNumber(float64(val)) 62 case int64: 63 l.PushNumber(float64(val)) 64 case int: 65 l.PushNumber(float64(val)) 66 67 case float32: 68 l.PushNumber(float64(val)) 69 case float64: 70 l.PushNumber(val) 71 72 case complex64: 73 forwardOnType(l, []float32{real(val), imag(val)}) 74 case complex128: 75 forwardOnType(l, []float64{real(val), imag(val)}) 76 77 default: 78 forwardOnReflect(l, val) 79 } 80 } 81 82 func forwardOnReflect(l *lua.State, val interface{}) { 83 switch v := reflect.ValueOf(val); v.Kind() { 84 case reflect.Array, reflect.Slice: 85 recurseOnFuncSlice(l, func(i int) interface{} { return v.Index(i).Interface() }, v.Len()) 86 87 case reflect.Map: 88 l.CreateTable(0, v.Len()) 89 for _, key := range v.MapKeys() { 90 mapKey := key.Interface() 91 mapVal := v.MapIndex(key).Interface() 92 forwardOnType(l, mapKey) 93 forwardOnType(l, mapVal) 94 l.RawSet(-3) 95 } 96 97 default: 98 lua.Errorf(l, "contains unsupported type: %T", val) 99 panic("unreachable") 100 } 101 } 102 103 // the hack of using a func(int)interface{} makes it that it is valid for any 104 // type of slice 105 func recurseOnFuncSlice(l *lua.State, input func(int) interface{}, n int) { 106 l.CreateTable(n, 0) 107 luaArray(l) 108 for i := 0; i < n; i++ { 109 forwardOnType(l, input(i)) 110 l.RawSetInt(-2, i+1) 111 } 112 }