feat: 初始提交 - Code Server Bridge完整实现
- OAuth认证系统(Gitea + Lua扩展) - Git自动化操作(本地/SSH远程) - 实时进度WebSocket推送 - 现代化Tab界面UI - Cobra CLI命令行(init/version/serve) - 完整构建系统(Makefile + Taskfile) - UPX压缩支持(体积减少70%)
This commit is contained in:
307
pkg/lua/engine.go
Normal file
307
pkg/lua/engine.go
Normal file
@@ -0,0 +1,307 @@
|
||||
package lua
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"reflect"
|
||||
|
||||
lua "github.com/yuin/gopher-lua"
|
||||
)
|
||||
|
||||
type Engine struct {
|
||||
L *lua.LState
|
||||
}
|
||||
|
||||
func New() *Engine {
|
||||
L := lua.NewState()
|
||||
return &Engine{L: L}
|
||||
}
|
||||
|
||||
func (e *Engine) Close() {
|
||||
e.L.Close()
|
||||
}
|
||||
|
||||
// RegisterAPI registers all Lua API modules (http, json, log, etc.)
|
||||
func (e *Engine) RegisterAPI() {
|
||||
RegisterHTTPModule(e.L)
|
||||
RegisterJSONModule(e.L)
|
||||
RegisterLogModule(e.L)
|
||||
RegisterUtilModule(e.L)
|
||||
}
|
||||
|
||||
func (e *Engine) LoadFile(path string) error {
|
||||
return e.L.DoFile(path)
|
||||
}
|
||||
|
||||
func (e *Engine) CallString(fn string, args ...lua.LValue) (string, error) {
|
||||
L := e.L
|
||||
|
||||
if err := L.CallByParam(lua.P{
|
||||
Fn: L.GetGlobal(fn),
|
||||
NRet: 1,
|
||||
Protect: true,
|
||||
}, args...); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
ret := L.Get(-1)
|
||||
L.Pop(1)
|
||||
return ret.String(), nil
|
||||
}
|
||||
|
||||
func (e *Engine) CallStruct(fn string, out any, args ...lua.LValue) error {
|
||||
L := e.L
|
||||
|
||||
f := L.GetGlobal(fn)
|
||||
if f.Type() != lua.LTFunction {
|
||||
return fmt.Errorf("lua function %s not found", fn)
|
||||
}
|
||||
|
||||
if err := L.CallByParam(lua.P{
|
||||
Fn: f,
|
||||
NRet: 1,
|
||||
Protect: true,
|
||||
}, args...); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ret := L.Get(-1)
|
||||
L.Pop(1)
|
||||
|
||||
table, ok := ret.(*lua.LTable)
|
||||
if !ok {
|
||||
return fmt.Errorf("lua function %s must return table", fn)
|
||||
}
|
||||
|
||||
return luaTableToStruct(table, out)
|
||||
}
|
||||
|
||||
func luaTableToStruct(t *lua.LTable, out any) error {
|
||||
v := reflect.ValueOf(out)
|
||||
if v.Kind() != reflect.Ptr {
|
||||
return errors.New("out must be pointer")
|
||||
}
|
||||
|
||||
v = v.Elem()
|
||||
|
||||
for i := 0; i < v.NumField(); i++ {
|
||||
field := v.Type().Field(i)
|
||||
key := field.Tag.Get("lua")
|
||||
if key == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
lv := t.RawGetString(key)
|
||||
if lv == lua.LNil {
|
||||
continue
|
||||
}
|
||||
|
||||
fv := v.Field(i)
|
||||
if err := setFieldFromLua(fv, lv); err != nil {
|
||||
return fmt.Errorf("field %s: %w", field.Name, err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// setFieldFromLua sets a reflect.Value field from a lua.LValue
|
||||
func setFieldFromLua(fv reflect.Value, lv lua.LValue) error {
|
||||
if !fv.CanSet() {
|
||||
return errors.New("field cannot be set")
|
||||
}
|
||||
|
||||
switch fv.Kind() {
|
||||
case reflect.String:
|
||||
fv.SetString(lv.String())
|
||||
|
||||
case reflect.Bool:
|
||||
fv.SetBool(lua.LVAsBool(lv))
|
||||
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
if n, ok := lv.(lua.LNumber); ok {
|
||||
fv.SetInt(int64(n))
|
||||
}
|
||||
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||||
if n, ok := lv.(lua.LNumber); ok {
|
||||
fv.SetUint(uint64(n))
|
||||
}
|
||||
|
||||
case reflect.Float32, reflect.Float64:
|
||||
if n, ok := lv.(lua.LNumber); ok {
|
||||
fv.SetFloat(float64(n))
|
||||
}
|
||||
|
||||
case reflect.Slice:
|
||||
if ltable, ok := lv.(*lua.LTable); ok {
|
||||
return setSliceFromLuaTable(fv, ltable)
|
||||
}
|
||||
|
||||
case reflect.Map:
|
||||
if ltable, ok := lv.(*lua.LTable); ok {
|
||||
return setMapFromLuaTable(fv, ltable)
|
||||
}
|
||||
|
||||
case reflect.Struct:
|
||||
if ltable, ok := lv.(*lua.LTable); ok {
|
||||
return luaTableToStruct(ltable, fv.Addr().Interface())
|
||||
}
|
||||
|
||||
case reflect.Ptr:
|
||||
if fv.IsNil() {
|
||||
fv.Set(reflect.New(fv.Type().Elem()))
|
||||
}
|
||||
return setFieldFromLua(fv.Elem(), lv)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// setSliceFromLuaTable converts a Lua table to a Go slice
|
||||
func setSliceFromLuaTable(fv reflect.Value, t *lua.LTable) error {
|
||||
length := t.Len()
|
||||
slice := reflect.MakeSlice(fv.Type(), length, length)
|
||||
|
||||
for i := 1; i <= length; i++ {
|
||||
lv := t.RawGetInt(i)
|
||||
if lv == lua.LNil {
|
||||
continue
|
||||
}
|
||||
|
||||
elem := slice.Index(i - 1)
|
||||
if err := setFieldFromLua(elem, lv); err != nil {
|
||||
return fmt.Errorf("index %d: %w", i, err)
|
||||
}
|
||||
}
|
||||
|
||||
fv.Set(slice)
|
||||
return nil
|
||||
}
|
||||
|
||||
// setMapFromLuaTable converts a Lua table to a Go map
|
||||
func setMapFromLuaTable(fv reflect.Value, t *lua.LTable) error {
|
||||
mapType := fv.Type()
|
||||
newMap := reflect.MakeMap(mapType)
|
||||
|
||||
var convErr error
|
||||
t.ForEach(func(key, value lua.LValue) {
|
||||
if convErr != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Convert key
|
||||
k := reflect.New(mapType.Key()).Elem()
|
||||
if err := setFieldFromLua(k, key); err != nil {
|
||||
convErr = fmt.Errorf("key conversion: %w", err)
|
||||
return
|
||||
}
|
||||
|
||||
// Convert value
|
||||
v := reflect.New(mapType.Elem()).Elem()
|
||||
if err := setFieldFromLua(v, value); err != nil {
|
||||
convErr = fmt.Errorf("value conversion: %w", err)
|
||||
return
|
||||
}
|
||||
|
||||
newMap.SetMapIndex(k, v)
|
||||
})
|
||||
|
||||
if convErr != nil {
|
||||
return convErr
|
||||
}
|
||||
|
||||
fv.Set(newMap)
|
||||
return nil
|
||||
}
|
||||
|
||||
// GoToLua converts a Go value to a Lua value
|
||||
func GoToLua(L *lua.LState, v any) lua.LValue {
|
||||
if v == nil {
|
||||
return lua.LNil
|
||||
}
|
||||
|
||||
val := reflect.ValueOf(v)
|
||||
return goValueToLua(L, val)
|
||||
}
|
||||
|
||||
// goValueToLua converts a reflect.Value to lua.LValue
|
||||
func goValueToLua(L *lua.LState, v reflect.Value) lua.LValue {
|
||||
if !v.IsValid() {
|
||||
return lua.LNil
|
||||
}
|
||||
|
||||
// Dereference pointers and interfaces
|
||||
for v.Kind() == reflect.Ptr || v.Kind() == reflect.Interface {
|
||||
if v.IsNil() {
|
||||
return lua.LNil
|
||||
}
|
||||
v = v.Elem()
|
||||
}
|
||||
|
||||
switch v.Kind() {
|
||||
case reflect.Bool:
|
||||
return lua.LBool(v.Bool())
|
||||
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
return lua.LNumber(v.Int())
|
||||
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||||
return lua.LNumber(v.Uint())
|
||||
|
||||
case reflect.Float32, reflect.Float64:
|
||||
return lua.LNumber(v.Float())
|
||||
|
||||
case reflect.String:
|
||||
return lua.LString(v.String())
|
||||
|
||||
case reflect.Slice, reflect.Array:
|
||||
table := L.NewTable()
|
||||
for i := 0; i < v.Len(); i++ {
|
||||
table.Append(goValueToLua(L, v.Index(i)))
|
||||
}
|
||||
return table
|
||||
|
||||
case reflect.Map:
|
||||
table := L.NewTable()
|
||||
iter := v.MapRange()
|
||||
for iter.Next() {
|
||||
key := iter.Key()
|
||||
val := goValueToLua(L, iter.Value())
|
||||
|
||||
// For string keys, use RawSetString for better Lua compatibility
|
||||
if key.Kind() == reflect.String {
|
||||
table.RawSetString(key.String(), val)
|
||||
} else {
|
||||
// For non-string keys, convert and use RawSet
|
||||
luaKey := goValueToLua(L, key)
|
||||
table.RawSet(luaKey, val)
|
||||
}
|
||||
}
|
||||
return table
|
||||
|
||||
case reflect.Struct:
|
||||
table := L.NewTable()
|
||||
t := v.Type()
|
||||
for i := 0; i < v.NumField(); i++ {
|
||||
field := t.Field(i)
|
||||
|
||||
// Skip unexported fields
|
||||
if !field.IsExported() {
|
||||
continue
|
||||
}
|
||||
|
||||
// Get lua tag or use field name
|
||||
luaKey := field.Tag.Get("lua")
|
||||
if luaKey == "" {
|
||||
luaKey = field.Name
|
||||
}
|
||||
|
||||
fieldValue := goValueToLua(L, v.Field(i))
|
||||
table.RawSetString(luaKey, fieldValue)
|
||||
}
|
||||
return table
|
||||
|
||||
default:
|
||||
return lua.LNil
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user