217 lines
7.2 KiB
Go
217 lines
7.2 KiB
Go
package processor
|
|
|
|
import (
|
|
"cook/utils"
|
|
"encoding/json"
|
|
"fmt"
|
|
"time"
|
|
|
|
logger "git.site.quack-lab.dev/dave/cylogger"
|
|
lua "github.com/yuin/gopher-lua"
|
|
)
|
|
|
|
// jsonLogger is a scoped logger for the processor/json package.
|
|
var jsonLogger = logger.Default.WithPrefix("processor/json")
|
|
|
|
// ProcessJSON applies Lua processing to JSON content
|
|
func ProcessJSON(content string, command utils.ModifyCommand, filename string) ([]utils.ReplaceCommand, error) {
|
|
processJsonLogger := jsonLogger.WithPrefix("ProcessJSON").WithField("commandName", command.Name).WithField("file", filename)
|
|
processJsonLogger.Debug("Starting JSON processing for file")
|
|
processJsonLogger.Trace("Initial file content length: %d", len(content))
|
|
|
|
var commands []utils.ReplaceCommand
|
|
startTime := time.Now()
|
|
|
|
// Parse JSON content
|
|
var jsonData interface{}
|
|
err := json.Unmarshal([]byte(content), &jsonData)
|
|
if err != nil {
|
|
processJsonLogger.Error("Failed to parse JSON content: %v", err)
|
|
return commands, fmt.Errorf("failed to parse JSON: %v", err)
|
|
}
|
|
processJsonLogger.Debug("Successfully parsed JSON content")
|
|
|
|
// Create Lua state
|
|
L, err := NewLuaState()
|
|
if err != nil {
|
|
processJsonLogger.Error("Error creating Lua state: %v", err)
|
|
return commands, fmt.Errorf("error creating Lua state: %v", err)
|
|
}
|
|
defer L.Close()
|
|
|
|
// Set filename global
|
|
L.SetGlobal("file", lua.LString(filename))
|
|
|
|
// Convert JSON data to Lua table
|
|
luaTable, err := ToLuaTable(L, jsonData)
|
|
if err != nil {
|
|
processJsonLogger.Error("Failed to convert JSON to Lua table: %v", err)
|
|
return commands, fmt.Errorf("failed to convert JSON to Lua table: %v", err)
|
|
}
|
|
|
|
// Set the JSON data as a global variable
|
|
L.SetGlobal("data", luaTable)
|
|
processJsonLogger.Debug("Set JSON data as Lua global 'data'")
|
|
|
|
// Build and execute Lua script for JSON mode
|
|
luaExpr := BuildJSONLuaScript(command.Lua)
|
|
processJsonLogger.Debug("Built Lua script from expression: %q", command.Lua)
|
|
processJsonLogger.Trace("Full Lua script: %q", utils.LimitString(luaExpr, 200))
|
|
|
|
if err := L.DoString(luaExpr); err != nil {
|
|
processJsonLogger.Error("Lua script execution failed: %v\nScript: %s", err, utils.LimitString(luaExpr, 200))
|
|
return commands, fmt.Errorf("lua script execution failed: %v", err)
|
|
}
|
|
processJsonLogger.Debug("Lua script executed successfully")
|
|
|
|
// Check if modification flag is set
|
|
modifiedVal := L.GetGlobal("modified")
|
|
if modifiedVal.Type() != lua.LTBool || !lua.LVAsBool(modifiedVal) {
|
|
processJsonLogger.Debug("Skipping - no modifications indicated by Lua script")
|
|
return commands, nil
|
|
}
|
|
|
|
// Get the modified data from Lua
|
|
modifiedData := L.GetGlobal("data")
|
|
if modifiedData.Type() != lua.LTTable {
|
|
processJsonLogger.Error("Expected 'data' to be a table after Lua processing, got %s", modifiedData.Type().String())
|
|
return commands, fmt.Errorf("expected 'data' to be a table after Lua processing")
|
|
}
|
|
|
|
// Convert back to Go interface
|
|
goData, err := FromLua(L, modifiedData)
|
|
if err != nil {
|
|
processJsonLogger.Error("Failed to convert Lua table back to Go: %v", err)
|
|
return commands, fmt.Errorf("failed to convert Lua table back to Go: %v", err)
|
|
}
|
|
|
|
// Marshal back to JSON
|
|
modifiedJSON, err := json.MarshalIndent(goData, "", " ")
|
|
if err != nil {
|
|
processJsonLogger.Error("Failed to marshal modified data to JSON: %v", err)
|
|
return commands, fmt.Errorf("failed to marshal modified data to JSON: %v", err)
|
|
}
|
|
|
|
// Create replacement command for the entire file
|
|
// For JSON mode, we always replace the entire content
|
|
commands = append(commands, utils.ReplaceCommand{
|
|
From: 0,
|
|
To: len(content),
|
|
With: string(modifiedJSON),
|
|
})
|
|
|
|
processJsonLogger.Debug("Total JSON processing time: %v", time.Since(startTime))
|
|
processJsonLogger.Debug("Generated %d total modifications", len(commands))
|
|
return commands, nil
|
|
}
|
|
|
|
// ToLuaTable converts a Go interface{} to a Lua table recursively
|
|
func ToLuaTable(L *lua.LState, data interface{}) (*lua.LTable, error) {
|
|
toLuaTableLogger := jsonLogger.WithPrefix("ToLuaTable")
|
|
toLuaTableLogger.Debug("Converting Go interface to Lua table")
|
|
toLuaTableLogger.Trace("Input data type: %T", data)
|
|
|
|
switch v := data.(type) {
|
|
case map[string]interface{}:
|
|
toLuaTableLogger.Debug("Converting map to Lua table")
|
|
table := L.CreateTable(0, len(v))
|
|
for key, value := range v {
|
|
luaValue, err := ToLuaValue(L, value)
|
|
if err != nil {
|
|
toLuaTableLogger.Error("Failed to convert map value for key %q: %v", key, err)
|
|
return nil, err
|
|
}
|
|
table.RawSetString(key, luaValue)
|
|
}
|
|
return table, nil
|
|
|
|
case []interface{}:
|
|
toLuaTableLogger.Debug("Converting slice to Lua table")
|
|
table := L.CreateTable(len(v), 0)
|
|
for i, value := range v {
|
|
luaValue, err := ToLuaValue(L, value)
|
|
if err != nil {
|
|
toLuaTableLogger.Error("Failed to convert slice value at index %d: %v", i, err)
|
|
return nil, err
|
|
}
|
|
table.RawSetInt(i+1, luaValue) // Lua arrays are 1-indexed
|
|
}
|
|
return table, nil
|
|
|
|
case string:
|
|
toLuaTableLogger.Debug("Converting string to Lua string")
|
|
return nil, fmt.Errorf("expected table or array, got string")
|
|
|
|
case float64:
|
|
toLuaTableLogger.Debug("Converting float64 to Lua number")
|
|
return nil, fmt.Errorf("expected table or array, got number")
|
|
|
|
case bool:
|
|
toLuaTableLogger.Debug("Converting bool to Lua boolean")
|
|
return nil, fmt.Errorf("expected table or array, got boolean")
|
|
|
|
case nil:
|
|
toLuaTableLogger.Debug("Converting nil to Lua nil")
|
|
return nil, fmt.Errorf("expected table or array, got nil")
|
|
|
|
default:
|
|
toLuaTableLogger.Error("Unsupported type for Lua table conversion: %T", v)
|
|
return nil, fmt.Errorf("unsupported type for Lua table conversion: %T", v)
|
|
}
|
|
}
|
|
|
|
// ToLuaValue converts a Go interface{} to a Lua value
|
|
func ToLuaValue(L *lua.LState, data interface{}) (lua.LValue, error) {
|
|
toLuaValueLogger := jsonLogger.WithPrefix("ToLuaValue")
|
|
toLuaValueLogger.Debug("Converting Go interface to Lua value")
|
|
toLuaValueLogger.Trace("Input data type: %T", data)
|
|
|
|
switch v := data.(type) {
|
|
case map[string]interface{}:
|
|
toLuaValueLogger.Debug("Converting map to Lua table")
|
|
table := L.CreateTable(0, len(v))
|
|
for key, value := range v {
|
|
luaValue, err := ToLuaValue(L, value)
|
|
if err != nil {
|
|
toLuaValueLogger.Error("Failed to convert map value for key %q: %v", key, err)
|
|
return lua.LNil, err
|
|
}
|
|
table.RawSetString(key, luaValue)
|
|
}
|
|
return table, nil
|
|
|
|
case []interface{}:
|
|
toLuaValueLogger.Debug("Converting slice to Lua table")
|
|
table := L.CreateTable(len(v), 0)
|
|
for i, value := range v {
|
|
luaValue, err := ToLuaValue(L, value)
|
|
if err != nil {
|
|
toLuaValueLogger.Error("Failed to convert slice value at index %d: %v", i, err)
|
|
return lua.LNil, err
|
|
}
|
|
table.RawSetInt(i+1, luaValue) // Lua arrays are 1-indexed
|
|
}
|
|
return table, nil
|
|
|
|
case string:
|
|
toLuaValueLogger.Debug("Converting string to Lua string")
|
|
return lua.LString(v), nil
|
|
|
|
case float64:
|
|
toLuaValueLogger.Debug("Converting float64 to Lua number")
|
|
return lua.LNumber(v), nil
|
|
|
|
case bool:
|
|
toLuaValueLogger.Debug("Converting bool to Lua boolean")
|
|
return lua.LBool(v), nil
|
|
|
|
case nil:
|
|
toLuaValueLogger.Debug("Converting nil to Lua nil")
|
|
return lua.LNil, nil
|
|
|
|
default:
|
|
toLuaValueLogger.Error("Unsupported type for Lua value conversion: %T", v)
|
|
return lua.LNil, fmt.Errorf("unsupported type for Lua value conversion: %T", v)
|
|
}
|
|
}
|