package main
import (
"fmt"
"log"
"os"
"reflect"
lua "github.com/yuin/gopher-lua"
)
func registerStrucAsLuaMetaTable[T any](
luaState *lua.LState,
checkStruct func(luaState *lua.LState) *T,
structType T,
metaTableName string,
) {
metaTable := luaState.NewTypeMetatable(metaTableName)
luaState.SetGlobal(metaTableName, metaTable)
luaState.SetField(
metaTable,
"new",
luaState.NewFunction(
newStructFunctionFactory(structType, metaTableName),
),
)
var zero [0]T
luaState.SetField(
metaTable,
"__index",
luaState.SetFuncs(
luaState.NewTable(),
luaTableGenFactory(reflect.TypeOf(zero),
checkStruct,
),
),
)
}
func newStructFunctionFactory[T any](structType T, metaTableName string) func(*lua.LState) int {
return func(luaState *lua.LState) int {
structInstance := &structType
ud := luaState.NewUserData()
ud.Value = structInstance
luaState.SetMetatable(ud, luaState.GetTypeMetatable(metaTableName))
luaState.Push(ud)
return 1
}
}
func checkStruct[T any](luaState *lua.LState) *T {
userData := luaState.CheckUserData(1)
if v, ok := userData.Value.(*T); ok {
return v
}
luaState.ArgError(1, "got wrong struct")
return nil
}
func getterSetterFactory[T any](
fieldName string,
fieldType reflect.Type,
checkStruct func(luaState *lua.LState) *T,
) func(*lua.LState) int {
return func(luaState *lua.LState) int {
genericStruct := checkStruct(luaState)
structValue := reflect.ValueOf(genericStruct).Elem()
fieldValue := structValue.FieldByName(fieldName)
if luaState.GetTop() == 2 { //nolint: mnd,gomnd
switch fieldType.Kind() {
case reflect.String:
fieldValue.SetString(luaState.CheckString(2)) //nolint: mnd,gomnd
case reflect.Float64:
fieldValue.SetFloat(float64(luaState.CheckNumber(2))) //nolint: mnd,gomnd
case reflect.Float32:
fieldValue.SetFloat(float64(luaState.CheckNumber(2))) //nolint: mnd,gomnd
case reflect.Int8:
fieldValue.SetInt(int64(luaState.CheckInt(2))) //nolint: mnd,gomnd
case reflect.Int16:
fieldValue.SetInt(int64(luaState.CheckInt(2))) //nolint: mnd,gomnd
case reflect.Int:
fieldValue.SetInt(int64(luaState.CheckInt(2))) //nolint: mnd,gomnd
case reflect.Int32:
fieldValue.SetInt(int64(luaState.CheckInt(2))) //nolint: mnd,gomnd
case reflect.Int64:
fieldValue.SetInt(int64(luaState.CheckInt(2))) //nolint: mnd,gomnd
case reflect.Bool:
fieldValue.SetBool(luaState.CheckBool(2)) //nolint: mnd,gomnd
default:
log.Print("unsupported type")
}
return 0
}
switch fieldType.Kind() {
case reflect.String:
luaState.Push(lua.LString(fieldValue.Interface().(string)))
case reflect.Float64:
luaState.Push(lua.LNumber(fieldValue.Interface().(float64)))
case reflect.Float32:
luaState.Push(lua.LNumber(fieldValue.Float()))
case reflect.Int8:
luaState.Push(lua.LNumber(fieldValue.Int()))
case reflect.Int16:
luaState.Push(lua.LNumber(fieldValue.Int()))
case reflect.Int:
luaState.Push(lua.LNumber(fieldValue.Int()))
case reflect.Int32:
luaState.Push(lua.LNumber(fieldValue.Int()))
case reflect.Int64:
luaState.Push(lua.LNumber(fieldValue.Int()))
case reflect.Bool:
luaState.Push(lua.LBool(fieldValue.Bool()))
default:
log.Print("unsupported type")
}
return 1
}
}
func luaTableGenFactory[T any](
structType reflect.Type,
checkStructType func(luaState *lua.LState) *T) map[string]lua.LGFunction {
tableMethods := make(map[string]lua.LGFunction)
for _, field := range reflect.VisibleFields(structType) {
tableMethods[field.Name] = getterSetterFactory(field.Name, field.Type, checkStructType)
}
return tableMethods
}
func RegisterCustomLuaTypes(luaState *lua.LState) {
registerStrucAsLuaMetaTable(luaState, checkStruct, TomlConfig{}, "toml_config")
registerStrucAsLuaMetaTable(luaState, checkStruct, CustomCommand{}, "custom_command")
}
func returnAllPlugins(pluginPath string) ([]string, error) {
pluginList := make([]string, 0)
files, err := os.ReadDir(pluginPath)
if err != nil {
return pluginList, fmt.Errorf("Error reading plugins directory: %v", err)
}
for _, file := range files {
pluginList = append(pluginList, file.Name())
}
return pluginList, nil
}
func LoadAllPlugins(appConfig *TomlConfig) {
luaState := lua.NewState()
defer luaState.Close()
RegisterCustomLuaTypes(luaState)
allPlugins, err := returnAllPlugins(appConfig.PluginPath)
if err != nil {
luaState.Close()
log.Fatal(err) //nolint: gocritic
}
log.Println(allPlugins)
}