Integrate the xml processing with the rest of the project

This commit is contained in:
2025-12-19 11:44:07 +01:00
parent f1ea0f9156
commit 74394cbde9
4 changed files with 380 additions and 22 deletions

View File

@@ -10,6 +10,7 @@ import (
"strings"
logger "git.site.quack-lab.dev/dave/cylogger"
lua "github.com/yuin/gopher-lua"
)
var xmlLogger = logger.Default.WithPrefix("processor/xml")
@@ -445,3 +446,149 @@ func formatNumeric(f float64) string {
}
return strconv.FormatFloat(f, 'f', -1, 64)
}
// ProcessXML applies Lua processing to XML content with surgical editing
func ProcessXML(content string, command utils.ModifyCommand, filename string) ([]utils.ReplaceCommand, error) {
processXMLLogger := xmlLogger.WithPrefix("ProcessXML").WithField("commandName", command.Name).WithField("file", filename)
processXMLLogger.Debug("Starting XML processing for file")
// Parse XML with position tracking
originalElem, err := parseXMLWithPositions(content)
if err != nil {
processXMLLogger.Error("Failed to parse XML: %v", err)
return nil, fmt.Errorf("failed to parse XML: %v", err)
}
processXMLLogger.Debug("Successfully parsed XML content")
// Create Lua state
L, err := NewLuaState()
if err != nil {
processXMLLogger.Error("Error creating Lua state: %v", err)
return nil, fmt.Errorf("error creating Lua state: %v", err)
}
defer L.Close()
// Set filename global
L.SetGlobal("file", lua.LString(filename))
// Create modifiable copy
modifiedElem := deepCopyXMLElement(originalElem)
// Convert to Lua table and set as global
luaTable := xmlElementToLuaTable(L, modifiedElem)
L.SetGlobal("root", luaTable)
processXMLLogger.Debug("Set XML data as Lua global 'root'")
// Build and execute Lua script
luaExpr := BuildJSONLuaScript(command.Lua) // Reuse JSON script builder
processXMLLogger.Debug("Built Lua script from expression: %q", command.Lua)
if err := L.DoString(luaExpr); err != nil {
processXMLLogger.Error("Lua script execution failed: %v\nScript: %s", err, luaExpr)
return nil, fmt.Errorf("lua script execution failed: %v", err)
}
processXMLLogger.Debug("Lua script executed successfully")
// Check if modification flag is set
modifiedVal := L.GetGlobal("modified")
if modifiedVal.Type() != lua.LTBool || !lua.LVAsBool(modifiedVal) {
processXMLLogger.Debug("Skipping - no modifications indicated by Lua script")
return nil, nil
}
// Get the modified data back from Lua
modifiedTable := L.GetGlobal("root")
if modifiedTable.Type() != lua.LTTable {
processXMLLogger.Error("Expected 'root' to be a table after Lua processing")
return nil, fmt.Errorf("expected 'root' to be a table after Lua processing")
}
// Apply Lua modifications back to XMLElement
luaTableToXMLElement(L, modifiedTable.(*lua.LTable), modifiedElem)
// Find changes between original and modified
changes := findXMLChanges(originalElem, modifiedElem, "")
processXMLLogger.Debug("Found %d changes", len(changes))
if len(changes) == 0 {
return nil, nil
}
// Generate surgical replace commands
commands := applyXMLChanges(changes)
processXMLLogger.Debug("Generated %d replace commands", len(commands))
return commands, nil
}
// xmlElementToLuaTable converts an XMLElement to a Lua table
func xmlElementToLuaTable(L *lua.LState, elem *XMLElement) *lua.LTable {
table := L.CreateTable(0, 4)
table.RawSetString("_tag", lua.LString(elem.Tag))
if len(elem.Attributes) > 0 {
attrs := L.CreateTable(0, len(elem.Attributes))
for name, attr := range elem.Attributes {
attrs.RawSetString(name, lua.LString(attr.Value))
}
table.RawSetString("_attr", attrs)
}
if elem.Text != "" {
table.RawSetString("_text", lua.LString(elem.Text))
}
if len(elem.Children) > 0 {
children := L.CreateTable(len(elem.Children), 0)
for i, child := range elem.Children {
children.RawSetInt(i+1, xmlElementToLuaTable(L, child))
}
table.RawSetString("_children", children)
}
return table
}
// luaTableToXMLElement applies Lua table modifications back to XMLElement
func luaTableToXMLElement(L *lua.LState, table *lua.LTable, elem *XMLElement) {
// Update text
if textVal := table.RawGetString("_text"); textVal.Type() == lua.LTString {
elem.Text = string(textVal.(lua.LString))
}
// Update attributes
if attrVal := table.RawGetString("_attr"); attrVal.Type() == lua.LTTable {
attrTable := attrVal.(*lua.LTable)
// Clear and rebuild attributes
elem.Attributes = make(map[string]XMLAttribute)
attrTable.ForEach(func(key lua.LValue, value lua.LValue) {
if key.Type() == lua.LTString && value.Type() == lua.LTString {
attrName := string(key.(lua.LString))
attrValue := string(value.(lua.LString))
elem.Attributes[attrName] = XMLAttribute{Value: attrValue}
}
})
}
// Update children
if childrenVal := table.RawGetString("_children"); childrenVal.Type() == lua.LTTable {
childrenTable := childrenVal.(*lua.LTable)
newChildren := []*XMLElement{}
// Iterate over array indices
for i := 1; ; i++ {
childVal := childrenTable.RawGetInt(i)
if childVal.Type() == lua.LTNil {
break
}
if childVal.Type() == lua.LTTable {
if i-1 < len(elem.Children) {
// Update existing child
luaTableToXMLElement(L, childVal.(*lua.LTable), elem.Children[i-1])
newChildren = append(newChildren, elem.Children[i-1])
}
}
}
elem.Children = newChildren
}
}