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  }