diff --git a/.vscode/launch.json b/.vscode/launch.json index 8ef3ce6..23f7764 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -9,8 +9,13 @@ "type": "go", "request": "launch", "mode": "auto", - "program": "${fileDirname}", - "args": [] + "program": "${workspaceFolder}", + "args": [ + "-mode=json", + "$..name", + "v='pero'", + "test.json" + ] } ] } \ No newline at end of file diff --git a/main.go b/main.go index a1a9ac0..e44668d 100644 --- a/main.go +++ b/main.go @@ -32,7 +32,6 @@ var logger *log.Logger var ( fileModeFlag = flag.String("mode", "regex", "Processing mode: regex, xml, json") - verboseFlag = flag.Bool("verbose", false, "Enable verbose output") ) func init() { @@ -48,12 +47,6 @@ func main() { fmt.Fprintf(os.Stderr, "\nOptions:\n") fmt.Fprintf(os.Stderr, " -mode string\n") fmt.Fprintf(os.Stderr, " Processing mode: regex, xml, json (default \"regex\")\n") - fmt.Fprintf(os.Stderr, " -xpath string\n") - fmt.Fprintf(os.Stderr, " XPath expression (for XML mode)\n") - fmt.Fprintf(os.Stderr, " -jsonpath string\n") - fmt.Fprintf(os.Stderr, " JSONPath expression (for JSON mode)\n") - fmt.Fprintf(os.Stderr, " -verbose\n") - fmt.Fprintf(os.Stderr, " Enable verbose output\n") fmt.Fprintf(os.Stderr, "\nExamples:\n") fmt.Fprintf(os.Stderr, " Regex mode (default):\n") fmt.Fprintf(os.Stderr, " %s \"(\\d+)\" \"*1.5\" data.xml\n", os.Args[0]) @@ -83,15 +76,9 @@ func main() { var pattern, luaExpr string var filePatterns []string - if *fileModeFlag == "regex" { - pattern = args[0] - luaExpr = args[1] - filePatterns = args[2:] - } else { - // For XML/JSON modes, pattern comes from flags - luaExpr = args[0] - filePatterns = args[1:] - } + pattern = args[0] + luaExpr = args[1] + filePatterns = args[2:] // Prepare the Lua expression originalLuaExpr := luaExpr @@ -124,11 +111,10 @@ func main() { // pattern = *xpathFlag // logger.Printf("Starting XML modifier with XPath %q, expression %q on %d files", // pattern, luaExpr, len(files)) - // case "json": - // proc = &processor.JSONProcessor{} - // pattern = *jsonpathFlag - // logger.Printf("Starting JSON modifier with JSONPath %q, expression %q on %d files", - // pattern, luaExpr, len(files)) + case "json": + proc = &processor.JSONProcessor{} + logger.Printf("Starting JSON modifier with JSONPath %q, expression %q on %d files", + pattern, luaExpr, len(files)) } var wg sync.WaitGroup diff --git a/processor/json.go b/processor/json.go index 77fc3ca..60d267e 100644 --- a/processor/json.go +++ b/processor/json.go @@ -3,6 +3,7 @@ package processor import ( "encoding/json" "fmt" + "log" "modify/processor/jsonpath" "os" "path/filepath" @@ -67,33 +68,43 @@ func (p *JSONProcessor) ProcessContent(content string, pattern string, luaExpr s return content, 0, 0, nil } - // Initialize Lua - L, err := NewLuaState() - if err != nil { - return content, len(nodes), 0, fmt.Errorf("error creating Lua state: %v", err) - } - defer L.Close() + for _, node := range nodes { + log.Printf("Processing node at path: %s with value: %v", node.Path, node.Value) - err = p.ToLua(L, nodes) - if err != nil { - return content, len(nodes), 0, fmt.Errorf("error converting to Lua: %v", err) - } + // Initialize Lua + L, err := NewLuaState() + if err != nil { + return content, len(nodes), 0, fmt.Errorf("error creating Lua state: %v", err) + } + defer L.Close() + log.Println("Lua state initialized successfully.") - // Execute Lua script - if err := L.DoString(luaExpr); err != nil { - return content, len(nodes), 0, fmt.Errorf("error executing Lua %s: %v", luaExpr, err) - } + err = p.ToLua(L, node.Value) + if err != nil { + return content, len(nodes), 0, fmt.Errorf("error converting to Lua: %v", err) + } + log.Printf("Converted node value to Lua: %v", node.Value) - // Get modified value - result, err := p.FromLua(L) - if err != nil { - return content, len(nodes), 0, fmt.Errorf("error getting result from Lua: %v", err) - } + // Execute Lua script + log.Printf("Executing Lua script: %s", luaExpr) + if err := L.DoString(luaExpr); err != nil { + return content, len(nodes), 0, fmt.Errorf("error executing Lua %s: %v", luaExpr, err) + } + log.Println("Lua script executed successfully.") - // Apply the modification to the JSON data - err = p.updateJSONValue(jsonData, pattern, result) - if err != nil { - return content, len(nodes), 0, fmt.Errorf("error updating JSON: %v", err) + // Get modified value + result, err := p.FromLua(L) + if err != nil { + return content, len(nodes), 0, fmt.Errorf("error getting result from Lua: %v", err) + } + log.Printf("Retrieved modified value from Lua: %v", result) + + // Apply the modification to the JSON data + err = p.updateJSONValue(jsonData, node.Path, result) + if err != nil { + return content, len(nodes), 0, fmt.Errorf("error updating JSON: %v", err) + } + log.Printf("Updated JSON at path: %s with new value: %v", node.Path, result) } // Convert the modified JSON back to a string with same formatting @@ -154,12 +165,16 @@ func detectJsonIndentation(content string) (string, error) { // updateJSONValue updates a value in the JSON structure based on its JSONPath func (p *JSONProcessor) updateJSONValue(jsonData interface{}, path string, newValue interface{}) error { + err := jsonpath.Set(jsonData, path, newValue) + if err != nil { + return fmt.Errorf("failed to update JSON value at path '%s': %w", path, err) + } return nil } // ToLua converts JSON values to Lua variables func (p *JSONProcessor) ToLua(L *lua.LState, data interface{}) error { - table, err := ToLuaTable(L, data) + table, err := ToLua(L, data) if err != nil { return err } @@ -170,5 +185,5 @@ func (p *JSONProcessor) ToLua(L *lua.LState, data interface{}) error { // FromLua retrieves values from Lua func (p *JSONProcessor) FromLua(L *lua.LState) (interface{}, error) { luaValue := L.GetGlobal("v") - return FromLuaTable(L, luaValue.(*lua.LTable)) + return FromLua(L, luaValue) } diff --git a/processor/processor.go b/processor/processor.go index e9f5636..e268a63 100644 --- a/processor/processor.go +++ b/processor/processor.go @@ -2,7 +2,7 @@ package processor import ( "fmt" - "reflect" + "strconv" "strings" lua "github.com/yuin/gopher-lua" @@ -52,65 +52,75 @@ func NewLuaState() (*lua.LState, error) { return L, nil } -// ToLuaTable converts a struct or map to a Lua table recursively -func ToLuaTable(L *lua.LState, data interface{}) (*lua.LTable, error) { - luaTable := L.NewTable() - +// ToLua converts a struct or map to a Lua table recursively +func ToLua(L *lua.LState, data interface{}) (lua.LValue, error) { switch v := data.(type) { case map[string]interface{}: + luaTable := L.NewTable() for key, value := range v { - luaValue, err := ToLuaTable(L, value) + luaValue, err := ToLua(L, value) if err != nil { return nil, err } luaTable.RawSetString(key, luaValue) } - case struct{}: - val := reflect.ValueOf(v) - for i := 0; i < val.NumField(); i++ { - field := val.Type().Field(i) - luaValue, err := ToLuaTable(L, val.Field(i).Interface()) + return luaTable, nil + case []interface{}: + luaTable := L.NewTable() + for i, value := range v { + luaValue, err := ToLua(L, value) if err != nil { return nil, err } - luaTable.RawSetString(field.Name, luaValue) + luaTable.RawSetInt(i+1, luaValue) // Lua arrays are 1-indexed } + return luaTable, nil case string: - luaTable.RawSetString("v", lua.LString(v)) + return lua.LString(v), nil case bool: - luaTable.RawSetString("v", lua.LBool(v)) + return lua.LBool(v), nil case float64: - luaTable.RawSetString("v", lua.LNumber(v)) + return lua.LNumber(v), nil default: return nil, fmt.Errorf("unsupported data type: %T", data) } - return luaTable, nil } -// FromLuaTable converts a Lua table to a struct or map recursively -func FromLuaTable(L *lua.LState, luaTable *lua.LTable) (map[string]interface{}, error) { - result := make(map[string]interface{}) - - luaTable.ForEach(func(key lua.LValue, value lua.LValue) { - switch v := value.(type) { - case *lua.LTable: - nestedMap, err := FromLuaTable(L, v) +// 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) { + 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 { - return + isArray = false + break } - result[key.String()] = nestedMap - case lua.LString: - result[key.String()] = string(v) - case lua.LBool: - result[key.String()] = bool(v) - case lua.LNumber: - result[key.String()] = float64(v) - default: - result[key.String()] = nil } - }) - - return result, nil + if isArray { + list := make([]interface{}, 0, len(result)) + for _, value := range result { + list = append(list, value) + } + return list, nil + } + return result, nil + case lua.LString: + return string(v), nil + case lua.LBool: + return bool(v), nil + case lua.LNumber: + return float64(v), nil + default: + return nil, nil + } } // InitLuaHelpers initializes common Lua helper functions