Update internal/luabridge/old-luabridge.go

This commit is contained in:
2026-05-24 18:16:06 +00:00
parent c9d6d38491
commit 2ae36905c9

View File

@@ -0,0 +1,308 @@
// luabridge.go - интеграция Lua как скриптового языка в fush shell
// Предоставляет API для вызова команд shell из Lua скриптов
// Регистрирует функции exec, pipe, ls, cd, pwd в окружении Lua
// Позволяет создавать сложные скрипты для автоматизации задач
package luabridge
import (
"fmt"
"os"
"os/exec"
"path/filepath"
"strings"
"sync/atomic"
lua "github.com/yuin/gopher-lua"
)
// ShellInterface определяет интерфейс для взаимодействия с shell
type ShellInterface interface {
GetEnv(key string) string
SetEnv(key, value string)
}
// Bridge управляет выполнением Lua скриптов
type Bridge struct {
scriptsDir string
shell ShellInterface
state *lua.LState
closed atomic.Bool
}
// New создает новый Lua мост
func New(scriptsDir string, shell ShellInterface) *Bridge {
b := &Bridge{
scriptsDir: scriptsDir,
shell: shell,
}
// Создаем Lua состояние
b.state = lua.NewState()
// Регистрируем функции для Lua
b.registerFunctions()
return b
}
// registerFunctions регистрирует функции для Lua
func (b *Bridge) registerFunctions() {
// Регистрируем функцию print
b.state.SetGlobal("print", b.state.NewFunction(func(L *lua.LState) int {
args := make([]interface{}, L.GetTop())
for i := 1; i <= L.GetTop(); i++ {
args[i-1] = L.Get(i)
}
fmt.Println(args...)
return 0
}))
// Регистрируем функцию getenv
b.state.SetGlobal("getenv", b.state.NewFunction(func(L *lua.LState) int {
key := L.CheckString(1)
value := b.shell.GetEnv(key)
L.Push(lua.LString(value))
return 1
}))
// Регистрируем функцию setenv
b.state.SetGlobal("setenv", b.state.NewFunction(func(L *lua.LState) int {
key := L.CheckString(1)
value := L.CheckString(2)
b.shell.SetEnv(key, value)
return 0
}))
// Регистрируем функцию exec для выполнения команд из Lua
b.state.SetGlobal("exec", b.state.NewFunction(func(L *lua.LState) int {
cmd := L.CheckString(1)
args := make([]string, 0)
// Собираем аргументы
for i := 2; i <= L.GetTop(); i++ {
args = append(args, L.CheckString(i))
}
// Выполняем команду
externalCmd := exec.Command(cmd, args...)
externalCmd.Stdout = os.Stdout
externalCmd.Stderr = os.Stderr
err := externalCmd.Run()
if err != nil {
L.Push(lua.LBool(false))
L.Push(lua.LString(err.Error()))
return 2
}
L.Push(lua.LBool(true))
return 1
}))
// Регистрируем функцию exec_output для получения вывода команды
b.state.SetGlobal("exec_output", b.state.NewFunction(func(L *lua.LState) int {
cmd := L.CheckString(1)
args := make([]string, 0)
for i := 2; i <= L.GetTop(); i++ {
args = append(args, L.CheckString(i))
}
externalCmd := exec.Command(cmd, args...)
output, err := externalCmd.Output()
if err != nil {
L.Push(lua.LNil)
L.Push(lua.LString(err.Error()))
return 2
}
L.Push(lua.LString(string(output)))
return 1
}))
// Регистрируем функцию pipe для выполнения команд с пайпом
b.state.SetGlobal("pipe", b.state.NewFunction(func(L *lua.LState) int {
cmd1 := L.CheckString(1)
cmd2 := L.CheckString(2)
// Разбиваем команды на части
parts1 := strings.Fields(cmd1)
parts2 := strings.Fields(cmd2)
if len(parts1) == 0 || len(parts2) == 0 {
L.Push(lua.LBool(false))
L.Push(lua.LString("пустая команда"))
return 2
}
// Создаем команды
cmd1Exec := exec.Command(parts1[0], parts1[1:]...)
cmd2Exec := exec.Command(parts2[0], parts2[1:]...)
// Создаем пайп
stdout, err := cmd1Exec.StdoutPipe()
if err != nil {
L.Push(lua.LBool(false))
L.Push(lua.LString(err.Error()))
return 2
}
cmd2Exec.Stdin = stdout
cmd2Exec.Stdout = os.Stdout
cmd2Exec.Stderr = os.Stderr
// Запускаем команды
if err := cmd1Exec.Start(); err != nil {
L.Push(lua.LBool(false))
L.Push(lua.LString(err.Error()))
return 2
}
if err := cmd2Exec.Start(); err != nil {
L.Push(lua.LBool(false))
L.Push(lua.LString(err.Error()))
return 2
}
// Ожидаем завершения
cmd1Exec.Wait()
cmd2Exec.Wait()
L.Push(lua.LBool(true))
return 1
}))
// Регистрируем функцию ls
b.state.SetGlobal("ls", b.state.NewFunction(func(L *lua.LState) int {
path := "."
if L.GetTop() > 0 {
path = L.CheckString(1)
}
dir, err := os.Open(path)
if err != nil {
L.Push(lua.LNil)
L.Push(lua.LString(err.Error()))
return 2
}
defer dir.Close()
files, err := dir.Readdir(-1)
if err != nil {
L.Push(lua.LNil)
L.Push(lua.LString(err.Error()))
return 2
}
// Создаем таблицу с результатами
tbl := L.NewTable()
for _, file := range files {
tbl.Append(lua.LString(file.Name()))
}
L.Push(tbl)
return 1
}))
// Регистрируем функцию cd
b.state.SetGlobal("cd", b.state.NewFunction(func(L *lua.LState) int {
path := L.GetTop() > 0
if !path {
path = true
// Передаем true, но нужен путь
_ = path
}
// Простая реализация cd через shell
dir := b.shell.GetEnv("HOME")
if L.GetTop() > 0 {
dir = L.CheckString(1)
}
if err := os.Chdir(dir); err != nil {
L.Push(lua.LBool(false))
L.Push(lua.LString(err.Error()))
return 2
}
L.Push(lua.LBool(true))
return 1
}))
// Регистрируем функцию pwd
b.state.SetGlobal("pwd", b.state.NewFunction(func(L *lua.LState) int {
dir, err := os.Getwd()
if err != nil {
L.Push(lua.LNil)
L.Push(lua.LString(err.Error()))
return 2
}
L.Push(lua.LString(dir))
return 1
}))
}
// ExecuteScript выполняет Lua скрипт
func (b *Bridge) ExecuteScript(name string, args []string) error {
if b.closed.Load() {
return fmt.Errorf("мост закрыт")
}
// Формируем путь к скрипту
scriptPath := filepath.Join(b.scriptsDir, name+".lua")
// Проверяем существование файла
if _, err := os.Stat(scriptPath); os.IsNotExist(err) {
return err
}
// Загружаем и выполняем скрипт
if err := b.state.DoFile(scriptPath); err != nil {
return err
}
// Вызываем функцию main если она существует
if fn := b.state.GetGlobal("main"); fn.Type() == lua.LTFunction {
// Преобразуем аргументы в Lua значения
luaArgs := make([]lua.LValue, len(args))
for i, arg := range args {
luaArgs[i] = lua.LString(arg)
}
if err := b.state.CallByParam(lua.P{
Fn: fn,
NRet: 0,
Protect: true,
}, luaArgs...); err != nil {
return err
}
}
return nil
}
// ExecuteString выполняет Lua код из строки
func (b *Bridge) ExecuteString(code string) error {
if b.closed.Load() {
return fmt.Errorf("мост закрыт")
}
return b.state.DoString(code)
}
// GetState возвращает Lua состояние
func (b *Bridge) GetState() *lua.LState {
return b.state
}
// Close закрывает Lua состояние
func (b *Bridge) Close() {
if b.closed.Load() {
return
}
b.closed.Store(true)
if b.state != nil {
b.state.Close()
}
}