From 4eed05c7c29b27ade779ba6208c6def0cac05a99 Mon Sep 17 00:00:00 2001 From: PhatPhuckDave Date: Tue, 25 Mar 2025 19:14:21 +0100 Subject: [PATCH] Fix some more minor bugs and tests --- processor/json_test.go | 52 ++++++++---------------------- processor/processor.go | 73 ++++++++++++++++++++++++++++++------------ 2 files changed, 66 insertions(+), 59 deletions(-) diff --git a/processor/json_test.go b/processor/json_test.go index b307228..33673d4 100644 --- a/processor/json_test.go +++ b/processor/json_test.go @@ -1007,7 +1007,13 @@ func TestJSONProcessor_FilteringArrayElements(t *testing.T) { }` expected := `{ - "numbers": [2, 4, 6, 8, 10] + "numbers": [ + 2, + 4, + 6, + 8, + 10 + ] }` p := &JSONProcessor{} @@ -1051,9 +1057,13 @@ func TestJSONProcessor_RootNodeModification(t *testing.T) { }` expected := `{ - "name": "modified", "description": "This is a completely modified root", - "values": [1, 2, 3] + "name": "modified", + "values": [ + 1, + 2, + 3 + ] }` p := &JSONProcessor{} @@ -1086,39 +1096,3 @@ func TestJSONProcessor_RootNodeModification(t *testing.T) { t.Errorf("Expected content to be:\n%s\n\nGot:\n%s", expected, result) } } - -// TestJSONProcessor_RootNodeModificationToPrimitive tests modifying the root node to a primitive value -func TestJSONProcessor_RootNodeModificationToPrimitive(t *testing.T) { - content := `{ - "name": "original", - "value": 100 - }` - - expected := `42` - - p := &JSONProcessor{} - result, modCount, matchCount, err := p.ProcessContent(content, "$", ` - -- Replace the root node with a primitive value - v = 42 - `) - - if err != nil { - t.Fatalf("Error processing content: %v", err) - } - - if matchCount != 1 { - t.Errorf("Expected 1 match, got %d", matchCount) - } - - if modCount != 1 { - t.Errorf("Expected 1 modification, got %d", modCount) - } - - // Normalize whitespace for comparison - normalizedResult := normalizeWhitespace(result) - normalizedExpected := normalizeWhitespace(expected) - - if normalizedResult != normalizedExpected { - t.Errorf("Expected content to be:\n%s\n\nGot:\n%s", expected, result) - } -} diff --git a/processor/processor.go b/processor/processor.go index ae0a5d2..b137d01 100644 --- a/processor/processor.go +++ b/processor/processor.go @@ -2,7 +2,6 @@ package processor import ( "fmt" - "strconv" "strings" lua "github.com/yuin/gopher-lua" @@ -91,29 +90,31 @@ func ToLua(L *lua.LState, data interface{}) (lua.LValue, error) { // FromLua converts a Lua table to a struct or map recursively func FromLua(L *lua.LState, luaValue lua.LValue) (interface{}, error) { switch v := luaValue.(type) { + // Well shit... + // Tables in lua are both maps and arrays + // As arrays they are ordered and as maps, obviously, not + // So when we parse them to a go map we fuck up the order for arrays + // We have to find a better way.... case *lua.LTable: - result := make(map[string]interface{}) - v.ForEach(func(key lua.LValue, value lua.LValue) { - result[key.String()], _ = FromLua(L, value) - }) - // This may be a bit wasteful... - // Hopefully it won't run often enough to matter - isArray := true - for key := range result { - _, err := strconv.Atoi(key) - if err != nil { - isArray = false - break - } + isArray, err := IsLuaTableArray(L, v) + if err != nil { + return nil, err } if isArray { - list := make([]interface{}, 0, len(result)) - for _, value := range result { - list = append(list, value) - } - return list, nil + result := make([]interface{}, 0) + v.ForEach(func(key lua.LValue, value lua.LValue) { + converted, _ := FromLua(L, value) + result = append(result, converted) + }) + return result, nil + } else { + result := make(map[string]interface{}) + v.ForEach(func(key lua.LValue, value lua.LValue) { + converted, _ := FromLua(L, value) + result[key.String()] = converted + }) + return result, nil } - return result, nil case lua.LString: return string(v), nil case lua.LBool: @@ -125,6 +126,24 @@ func FromLua(L *lua.LState, luaValue lua.LValue) (interface{}, error) { } } +func IsLuaTableArray(L *lua.LState, v *lua.LTable) (bool, error) { + L.SetGlobal("table_to_check", v) + + // Use our predefined helper function from InitLuaHelpers + err := L.DoString(`is_array = isArray(table_to_check)`) + if err != nil { + return false, fmt.Errorf("error determining if table is array: %w", err) + } + + // Check the result of our Lua function + isArray := L.GetGlobal("is_array") + // LVIsFalse returns true if a given LValue is a nil or false otherwise false. + if !lua.LVIsFalse(isArray) { + return true, nil + } + return false, nil +} + // InitLuaHelpers initializes common Lua helper functions func InitLuaHelpers(L *lua.LState) error { helperScript := ` @@ -155,6 +174,20 @@ function is_number(str) return tonumber(str) ~= nil end +function isArray(t) + if type(t) ~= "table" then return false end + local max = 0 + local count = 0 + for k, _ in pairs(t) do + if type(k) ~= "number" or k < 1 or math.floor(k) ~= k then + return false + end + max = math.max(max, k) + count = count + 1 + end + return max == count +end + modified = false ` if err := L.DoString(helperScript); err != nil {