Improve error handling across the board
This commit is contained in:
8
main.go
8
main.go
@@ -97,7 +97,7 @@ func main() {
|
|||||||
originalLuaExpr := luaExpr
|
originalLuaExpr := luaExpr
|
||||||
luaExpr = processor.BuildLuaScript(luaExpr)
|
luaExpr = processor.BuildLuaScript(luaExpr)
|
||||||
if originalLuaExpr != luaExpr {
|
if originalLuaExpr != luaExpr {
|
||||||
logger.Printf("Transformed Lua expression from '%s' to '%s'", originalLuaExpr, luaExpr)
|
logger.Printf("Transformed Lua expression from %q to %q", originalLuaExpr, luaExpr)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Expand file patterns with glob support
|
// Expand file patterns with glob support
|
||||||
@@ -117,17 +117,17 @@ func main() {
|
|||||||
switch *fileModeFlag {
|
switch *fileModeFlag {
|
||||||
case "regex":
|
case "regex":
|
||||||
proc = &processor.RegexProcessor{}
|
proc = &processor.RegexProcessor{}
|
||||||
logger.Printf("Starting regex modifier with pattern '%s', expression '%s' on %d files",
|
logger.Printf("Starting regex modifier with pattern %q, expression %q on %d files",
|
||||||
pattern, luaExpr, len(files))
|
pattern, luaExpr, len(files))
|
||||||
// case "xml":
|
// case "xml":
|
||||||
// proc = &processor.XMLProcessor{}
|
// proc = &processor.XMLProcessor{}
|
||||||
// pattern = *xpathFlag
|
// pattern = *xpathFlag
|
||||||
// logger.Printf("Starting XML modifier with XPath '%s', expression '%s' on %d files",
|
// logger.Printf("Starting XML modifier with XPath %q, expression %q on %d files",
|
||||||
// pattern, luaExpr, len(files))
|
// pattern, luaExpr, len(files))
|
||||||
// case "json":
|
// case "json":
|
||||||
// proc = &processor.JSONProcessor{}
|
// proc = &processor.JSONProcessor{}
|
||||||
// pattern = *jsonpathFlag
|
// pattern = *jsonpathFlag
|
||||||
// logger.Printf("Starting JSON modifier with JSONPath '%s', expression '%s' on %d files",
|
// logger.Printf("Starting JSON modifier with JSONPath %q, expression %q on %d files",
|
||||||
// pattern, luaExpr, len(files))
|
// pattern, luaExpr, len(files))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -57,9 +57,9 @@ func (p *JSONProcessor) ProcessContent(content string, pattern string, luaExpr s
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Find nodes matching the JSONPath pattern
|
// Find nodes matching the JSONPath pattern
|
||||||
nodes := jsonpath.Get(jsonData, pattern)
|
nodes, err := jsonpath.Get(jsonData, pattern)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return content, 0, 0, fmt.Errorf("error executing JSONPath: %v", err)
|
return content, 0, 0, fmt.Errorf("error getting nodes: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
matchCount := len(nodes)
|
matchCount := len(nodes)
|
||||||
|
@@ -2,7 +2,6 @@ package jsonpath
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
|
||||||
"strconv"
|
"strconv"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -42,7 +41,7 @@ const (
|
|||||||
// ParseJSONPath parses a JSONPath string into a sequence of steps
|
// ParseJSONPath parses a JSONPath string into a sequence of steps
|
||||||
func ParseJSONPath(path string) ([]JSONStep, error) {
|
func ParseJSONPath(path string) ([]JSONStep, error) {
|
||||||
if len(path) == 0 || path[0] != '$' {
|
if len(path) == 0 || path[0] != '$' {
|
||||||
return nil, fmt.Errorf("path must start with $")
|
return nil, fmt.Errorf("path must start with $; received: %q", path)
|
||||||
}
|
}
|
||||||
|
|
||||||
steps := []JSONStep{}
|
steps := []JSONStep{}
|
||||||
@@ -80,13 +79,13 @@ func ParseJSONPath(path string) ([]JSONStep, error) {
|
|||||||
} else {
|
} else {
|
||||||
index, err := strconv.Atoi(indexStr)
|
index, err := strconv.Atoi(indexStr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("invalid index: %s", indexStr)
|
return nil, fmt.Errorf("invalid index: %s; error: %w", indexStr, err)
|
||||||
}
|
}
|
||||||
steps = append(steps, JSONStep{Type: IndexStep, Index: index})
|
steps = append(steps, JSONStep{Type: IndexStep, Index: index})
|
||||||
}
|
}
|
||||||
i = nextPos + 1 // Skip closing ]
|
i = nextPos + 1 // Skip closing ]
|
||||||
default:
|
default:
|
||||||
return nil, fmt.Errorf("unexpected character: %c", path[i])
|
return nil, fmt.Errorf("unexpected character: %c at position %d; path: %q", path[i], i, path)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -117,73 +116,77 @@ func readIndex(path string, start int) (string, int) {
|
|||||||
|
|
||||||
// Get retrieves values with their paths from data at the specified JSONPath
|
// Get retrieves values with their paths from data at the specified JSONPath
|
||||||
// Each returned JSONNode contains both the value and its exact path in the data structure
|
// Each returned JSONNode contains both the value and its exact path in the data structure
|
||||||
func Get(data interface{}, path string) []JSONNode {
|
func Get(data interface{}, path string) ([]JSONNode, error) {
|
||||||
steps, err := ParseJSONPath(path)
|
steps, err := ParseJSONPath(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println("Error parsing JSONPath:", err)
|
return nil, fmt.Errorf("failed to parse JSONPath %q: %w", path, err)
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
results := []JSONNode{}
|
results := []JSONNode{}
|
||||||
traverseWithPaths(data, steps, &results, "$")
|
err = traverseWithPaths(data, steps, &results, "$")
|
||||||
return results
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to traverse JSONPath %q: %w", path, err)
|
||||||
|
}
|
||||||
|
return results, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set updates the value at the specified JSONPath in the original data structure.
|
// Set updates the value at the specified JSONPath in the original data structure.
|
||||||
// It only modifies the first matching node.
|
// It only modifies the first matching node.
|
||||||
func Set(data interface{}, path string, value interface{}) bool {
|
func Set(data interface{}, path string, value interface{}) error {
|
||||||
steps, err := ParseJSONPath(path)
|
steps, err := ParseJSONPath(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println("Error parsing JSONPath:", err)
|
return fmt.Errorf("failed to parse JSONPath %q: %w", path, err)
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(steps) <= 1 {
|
if len(steps) <= 1 {
|
||||||
log.Println("Cannot set root node")
|
return fmt.Errorf("cannot set root node; the provided path %q is invalid", path)
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
success := false
|
success := false
|
||||||
setWithPath(data, steps, &success, value, "$", ModifyFirstMode)
|
err = setWithPath(data, steps, &success, value, "$", ModifyFirstMode)
|
||||||
return success
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to set value at JSONPath %q: %w", path, err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetAll updates all matching values at the specified JSONPath.
|
// SetAll updates all matching values at the specified JSONPath.
|
||||||
func SetAll(data interface{}, path string, value interface{}) bool {
|
func SetAll(data interface{}, path string, value interface{}) error {
|
||||||
steps, err := ParseJSONPath(path)
|
steps, err := ParseJSONPath(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println("Error parsing JSONPath:", err)
|
return fmt.Errorf("failed to parse JSONPath %q: %w", path, err)
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(steps) <= 1 {
|
if len(steps) <= 1 {
|
||||||
log.Println("Cannot set root node")
|
return fmt.Errorf("cannot set root node; the provided path %q is invalid", path)
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
success := false
|
success := false
|
||||||
setWithPath(data, steps, &success, value, "$", ModifyAllMode)
|
err = setWithPath(data, steps, &success, value, "$", ModifyAllMode)
|
||||||
return success
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to set value at JSONPath %q: %w", path, err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// setWithPath modifies values while tracking paths
|
// setWithPath modifies values while tracking paths
|
||||||
func setWithPath(node interface{}, steps []JSONStep, success *bool, value interface{}, currentPath string, mode TraversalMode) {
|
func setWithPath(node interface{}, steps []JSONStep, success *bool, value interface{}, currentPath string, mode TraversalMode) error {
|
||||||
if node == nil || *success && mode == ModifyFirstMode {
|
if node == nil || *success && mode == ModifyFirstMode {
|
||||||
return
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Skip root step
|
// Skip root step
|
||||||
actualSteps := steps
|
actualSteps := steps
|
||||||
if len(steps) > 0 && steps[0].Type == RootStep {
|
if len(steps) > 0 && steps[0].Type == RootStep {
|
||||||
if len(steps) == 1 {
|
if len(steps) == 1 {
|
||||||
return // Cannot set root node
|
return fmt.Errorf("cannot set root node; the provided path %q is invalid", currentPath)
|
||||||
}
|
}
|
||||||
actualSteps = steps[1:]
|
actualSteps = steps[1:]
|
||||||
}
|
}
|
||||||
|
|
||||||
// Process the first step
|
// Process the first step
|
||||||
if len(actualSteps) == 0 {
|
if len(actualSteps) == 0 {
|
||||||
return
|
return fmt.Errorf("cannot set root node; no steps provided for path %q", currentPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
step := actualSteps[0]
|
step := actualSteps[0]
|
||||||
@@ -194,7 +197,7 @@ func setWithPath(node interface{}, steps []JSONStep, success *bool, value interf
|
|||||||
case ChildStep:
|
case ChildStep:
|
||||||
m, ok := node.(map[string]interface{})
|
m, ok := node.(map[string]interface{})
|
||||||
if !ok {
|
if !ok {
|
||||||
return
|
return fmt.Errorf("node at path %q is not a map; actual type: %T", currentPath, node)
|
||||||
}
|
}
|
||||||
|
|
||||||
childPath := currentPath + "." + step.Key
|
childPath := currentPath + "." + step.Key
|
||||||
@@ -203,7 +206,7 @@ func setWithPath(node interface{}, steps []JSONStep, success *bool, value interf
|
|||||||
// We've reached the target, set the value
|
// We've reached the target, set the value
|
||||||
m[step.Key] = value
|
m[step.Key] = value
|
||||||
*success = true
|
*success = true
|
||||||
return
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create intermediate nodes if necessary
|
// Create intermediate nodes if necessary
|
||||||
@@ -218,12 +221,15 @@ func setWithPath(node interface{}, steps []JSONStep, success *bool, value interf
|
|||||||
m[step.Key] = child
|
m[step.Key] = child
|
||||||
}
|
}
|
||||||
|
|
||||||
setWithPath(child, remainingSteps, success, value, childPath, mode)
|
err := setWithPath(child, remainingSteps, success, value, childPath, mode)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to set value at JSONPath %q: %w", childPath, err)
|
||||||
|
}
|
||||||
|
|
||||||
case IndexStep:
|
case IndexStep:
|
||||||
arr, ok := node.([]interface{})
|
arr, ok := node.([]interface{})
|
||||||
if !ok {
|
if !ok {
|
||||||
return
|
return fmt.Errorf("node at path %q is not an array; actual type: %T", currentPath, node)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle wildcard index
|
// Handle wildcard index
|
||||||
@@ -234,16 +240,19 @@ func setWithPath(node interface{}, steps []JSONStep, success *bool, value interf
|
|||||||
arr[i] = value
|
arr[i] = value
|
||||||
*success = true
|
*success = true
|
||||||
if mode == ModifyFirstMode {
|
if mode == ModifyFirstMode {
|
||||||
return
|
return nil
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
setWithPath(item, remainingSteps, success, value, itemPath, mode)
|
err := setWithPath(item, remainingSteps, success, value, itemPath, mode)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to set value at JSONPath %q: %w", itemPath, err)
|
||||||
|
}
|
||||||
if *success && mode == ModifyFirstMode {
|
if *success && mode == ModifyFirstMode {
|
||||||
return
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle specific index
|
// Handle specific index
|
||||||
@@ -254,7 +263,10 @@ func setWithPath(node interface{}, steps []JSONStep, success *bool, value interf
|
|||||||
arr[step.Index] = value
|
arr[step.Index] = value
|
||||||
*success = true
|
*success = true
|
||||||
} else {
|
} else {
|
||||||
setWithPath(item, remainingSteps, success, value, itemPath, mode)
|
err := setWithPath(item, remainingSteps, success, value, itemPath, mode)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to set value at JSONPath %q: %w", itemPath, err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -267,12 +279,15 @@ func setWithPath(node interface{}, steps []JSONStep, success *bool, value interf
|
|||||||
m[step.Key] = value
|
m[step.Key] = value
|
||||||
*success = true
|
*success = true
|
||||||
if mode == ModifyFirstMode {
|
if mode == ModifyFirstMode {
|
||||||
return
|
return nil
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
setWithPath(val, remainingSteps, success, value, directPath, mode)
|
err := setWithPath(val, remainingSteps, success, value, directPath, mode)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to set value at JSONPath %q: %w", directPath, err)
|
||||||
|
}
|
||||||
if *success && mode == ModifyFirstMode {
|
if *success && mode == ModifyFirstMode {
|
||||||
return
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -287,17 +302,23 @@ func setWithPath(node interface{}, steps []JSONStep, success *bool, value interf
|
|||||||
if step.Key != "*" && k == step.Key {
|
if step.Key != "*" && k == step.Key {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
setWithPath(v, steps, success, value, childPath, mode) // Use the same steps
|
err := setWithPath(v, steps, success, value, childPath, mode)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to set value at JSONPath %q: %w", childPath, err)
|
||||||
|
}
|
||||||
if *success && mode == ModifyFirstMode {
|
if *success && mode == ModifyFirstMode {
|
||||||
return
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case []interface{}:
|
case []interface{}:
|
||||||
for i, v := range n {
|
for i, v := range n {
|
||||||
childPath := fmt.Sprintf("%s[%d]", currentPath, i)
|
childPath := fmt.Sprintf("%s[%d]", currentPath, i)
|
||||||
setWithPath(v, steps, success, value, childPath, mode) // Use the same steps
|
err := setWithPath(v, steps, success, value, childPath, mode)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to set value at JSONPath %q: %w", childPath, err)
|
||||||
|
}
|
||||||
if *success && mode == ModifyFirstMode {
|
if *success && mode == ModifyFirstMode {
|
||||||
return
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -305,7 +326,7 @@ func setWithPath(node interface{}, steps []JSONStep, success *bool, value interf
|
|||||||
case WildcardStep:
|
case WildcardStep:
|
||||||
m, ok := node.(map[string]interface{})
|
m, ok := node.(map[string]interface{})
|
||||||
if !ok {
|
if !ok {
|
||||||
return
|
return fmt.Errorf("node at path %q is not a map; actual type: %T", currentPath, node)
|
||||||
}
|
}
|
||||||
|
|
||||||
for k, v := range m {
|
for k, v := range m {
|
||||||
@@ -314,22 +335,26 @@ func setWithPath(node interface{}, steps []JSONStep, success *bool, value interf
|
|||||||
m[k] = value
|
m[k] = value
|
||||||
*success = true
|
*success = true
|
||||||
if mode == ModifyFirstMode {
|
if mode == ModifyFirstMode {
|
||||||
return
|
return nil
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
setWithPath(v, remainingSteps, success, value, childPath, mode)
|
err := setWithPath(v, remainingSteps, success, value, childPath, mode)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to set value at JSONPath %q: %w", childPath, err)
|
||||||
|
}
|
||||||
if *success && mode == ModifyFirstMode {
|
if *success && mode == ModifyFirstMode {
|
||||||
return
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// traverseWithPaths tracks both nodes and their paths during traversal
|
// traverseWithPaths tracks both nodes and their paths during traversal
|
||||||
func traverseWithPaths(node interface{}, steps []JSONStep, results *[]JSONNode, currentPath string) {
|
func traverseWithPaths(node interface{}, steps []JSONStep, results *[]JSONNode, currentPath string) error {
|
||||||
if len(steps) == 0 || node == nil {
|
if len(steps) == 0 || node == nil {
|
||||||
return
|
return fmt.Errorf("cannot traverse with empty steps or nil node; steps length: %d, node: %v", len(steps), node)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Skip root step
|
// Skip root step
|
||||||
@@ -337,7 +362,7 @@ func traverseWithPaths(node interface{}, steps []JSONStep, results *[]JSONNode,
|
|||||||
if steps[0].Type == RootStep {
|
if steps[0].Type == RootStep {
|
||||||
if len(steps) == 1 {
|
if len(steps) == 1 {
|
||||||
*results = append(*results, JSONNode{Value: node, Path: currentPath})
|
*results = append(*results, JSONNode{Value: node, Path: currentPath})
|
||||||
return
|
return nil
|
||||||
}
|
}
|
||||||
actualSteps = steps[1:]
|
actualSteps = steps[1:]
|
||||||
}
|
}
|
||||||
@@ -351,25 +376,28 @@ func traverseWithPaths(node interface{}, steps []JSONStep, results *[]JSONNode,
|
|||||||
case ChildStep:
|
case ChildStep:
|
||||||
m, ok := node.(map[string]interface{})
|
m, ok := node.(map[string]interface{})
|
||||||
if !ok {
|
if !ok {
|
||||||
return
|
return fmt.Errorf("node is not a map; actual type: %T", node)
|
||||||
}
|
}
|
||||||
|
|
||||||
child, exists := m[step.Key]
|
child, exists := m[step.Key]
|
||||||
if !exists {
|
if !exists {
|
||||||
return
|
return fmt.Errorf("key not found: %s in node at path: %s", step.Key, currentPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
childPath := currentPath + "." + step.Key
|
childPath := currentPath + "." + step.Key
|
||||||
if isLastStep {
|
if isLastStep {
|
||||||
*results = append(*results, JSONNode{Value: child, Path: childPath})
|
*results = append(*results, JSONNode{Value: child, Path: childPath})
|
||||||
} else {
|
} else {
|
||||||
traverseWithPaths(child, remainingSteps, results, childPath)
|
err := traverseWithPaths(child, remainingSteps, results, childPath)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to traverse JSONPath %q: %w", childPath, err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
case IndexStep:
|
case IndexStep:
|
||||||
arr, ok := node.([]interface{})
|
arr, ok := node.([]interface{})
|
||||||
if !ok {
|
if !ok {
|
||||||
return
|
return fmt.Errorf("node is not an array; actual type: %T", node)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle wildcard index
|
// Handle wildcard index
|
||||||
@@ -379,10 +407,13 @@ func traverseWithPaths(node interface{}, steps []JSONStep, results *[]JSONNode,
|
|||||||
if isLastStep {
|
if isLastStep {
|
||||||
*results = append(*results, JSONNode{Value: item, Path: itemPath})
|
*results = append(*results, JSONNode{Value: item, Path: itemPath})
|
||||||
} else {
|
} else {
|
||||||
traverseWithPaths(item, remainingSteps, results, itemPath)
|
err := traverseWithPaths(item, remainingSteps, results, itemPath)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to traverse JSONPath %q: %w", itemPath, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle specific index
|
// Handle specific index
|
||||||
@@ -392,9 +423,14 @@ func traverseWithPaths(node interface{}, steps []JSONStep, results *[]JSONNode,
|
|||||||
if isLastStep {
|
if isLastStep {
|
||||||
*results = append(*results, JSONNode{Value: item, Path: itemPath})
|
*results = append(*results, JSONNode{Value: item, Path: itemPath})
|
||||||
} else {
|
} else {
|
||||||
traverseWithPaths(item, remainingSteps, results, itemPath)
|
err := traverseWithPaths(item, remainingSteps, results, itemPath)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to traverse JSONPath %q: %w", itemPath, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
return fmt.Errorf("index %d out of bounds for array at path: %s", step.Index, currentPath)
|
||||||
|
}
|
||||||
|
|
||||||
case RecursiveDescentStep:
|
case RecursiveDescentStep:
|
||||||
// For recursive descent, first check direct match at this level
|
// For recursive descent, first check direct match at this level
|
||||||
@@ -404,7 +440,10 @@ func traverseWithPaths(node interface{}, steps []JSONStep, results *[]JSONNode,
|
|||||||
if isLastStep {
|
if isLastStep {
|
||||||
*results = append(*results, JSONNode{Value: val, Path: directPath})
|
*results = append(*results, JSONNode{Value: val, Path: directPath})
|
||||||
} else {
|
} else {
|
||||||
traverseWithPaths(val, remainingSteps, results, directPath)
|
err := traverseWithPaths(val, remainingSteps, results, directPath)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to traverse JSONPath %q: %w", directPath, err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -419,19 +458,25 @@ func traverseWithPaths(node interface{}, steps []JSONStep, results *[]JSONNode,
|
|||||||
case map[string]interface{}:
|
case map[string]interface{}:
|
||||||
for k, v := range n {
|
for k, v := range n {
|
||||||
childPath := currentPath + "." + k
|
childPath := currentPath + "." + k
|
||||||
traverseWithPaths(v, steps, results, childPath) // Use the same steps
|
err := traverseWithPaths(v, steps, results, childPath) // Use the same steps
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to traverse JSONPath %q: %w", childPath, err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
case []interface{}:
|
case []interface{}:
|
||||||
for i, v := range n {
|
for i, v := range n {
|
||||||
childPath := fmt.Sprintf("%s[%d]", currentPath, i)
|
childPath := fmt.Sprintf("%s[%d]", currentPath, i)
|
||||||
traverseWithPaths(v, steps, results, childPath) // Use the same steps
|
err := traverseWithPaths(v, steps, results, childPath) // Use the same steps
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to traverse JSONPath %q: %w", childPath, err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
case WildcardStep:
|
case WildcardStep:
|
||||||
m, ok := node.(map[string]interface{})
|
m, ok := node.(map[string]interface{})
|
||||||
if !ok {
|
if !ok {
|
||||||
return
|
return fmt.Errorf("node is not a map; actual type: %T", node)
|
||||||
}
|
}
|
||||||
|
|
||||||
for k, v := range m {
|
for k, v := range m {
|
||||||
@@ -439,8 +484,12 @@ func traverseWithPaths(node interface{}, steps []JSONStep, results *[]JSONNode,
|
|||||||
if isLastStep {
|
if isLastStep {
|
||||||
*results = append(*results, JSONNode{Value: v, Path: childPath})
|
*results = append(*results, JSONNode{Value: v, Path: childPath})
|
||||||
} else {
|
} else {
|
||||||
traverseWithPaths(v, remainingSteps, results, childPath)
|
err := traverseWithPaths(v, remainingSteps, results, childPath)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to traverse JSONPath %q: %w", childPath, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
@@ -11,6 +11,7 @@ func TestGetWithPathsBasic(t *testing.T) {
|
|||||||
data map[string]interface{}
|
data map[string]interface{}
|
||||||
path string
|
path string
|
||||||
expected []JSONNode
|
expected []JSONNode
|
||||||
|
error bool
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "simple property",
|
name: "simple property",
|
||||||
@@ -91,12 +92,19 @@ func TestGetWithPathsBasic(t *testing.T) {
|
|||||||
},
|
},
|
||||||
path: "$.user.email",
|
path: "$.user.email",
|
||||||
expected: []JSONNode{},
|
expected: []JSONNode{},
|
||||||
|
error: true,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
result := Get(tt.data, tt.path)
|
result, err := Get(tt.data, tt.path)
|
||||||
|
if err != nil {
|
||||||
|
if !tt.error {
|
||||||
|
t.Errorf("GetWithPaths() returned error: %v", err)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// For nonexistent path, we expect empty slice
|
// For nonexistent path, we expect empty slice
|
||||||
if tt.name == "nonexistent path" {
|
if tt.name == "nonexistent path" {
|
||||||
@@ -148,11 +156,12 @@ func TestSet(t *testing.T) {
|
|||||||
"name": "John",
|
"name": "John",
|
||||||
"age": 30,
|
"age": 30,
|
||||||
}
|
}
|
||||||
success := Set(data, "$.name", "Jane")
|
err := Set(data, "$.name", "Jane")
|
||||||
|
if err != nil {
|
||||||
if !success {
|
t.Errorf("Set() returned error: %v", err)
|
||||||
t.Errorf("Set() returned false, expected true")
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if data["name"] != "Jane" {
|
if data["name"] != "Jane" {
|
||||||
t.Errorf("Set() failed: expected name to be 'Jane', got %v", data["name"])
|
t.Errorf("Set() failed: expected name to be 'Jane', got %v", data["name"])
|
||||||
}
|
}
|
||||||
@@ -165,11 +174,12 @@ func TestSet(t *testing.T) {
|
|||||||
"age": 30,
|
"age": 30,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
success := Set(data, "$.user.name", "Jane")
|
err := Set(data, "$.user.name", "Jane")
|
||||||
|
if err != nil {
|
||||||
if !success {
|
t.Errorf("Set() returned error: %v", err)
|
||||||
t.Errorf("Set() returned false, expected true")
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
user, ok := data["user"].(map[string]interface{})
|
user, ok := data["user"].(map[string]interface{})
|
||||||
if !ok {
|
if !ok {
|
||||||
t.Fatalf("User is not a map")
|
t.Fatalf("User is not a map")
|
||||||
@@ -186,10 +196,10 @@ func TestSet(t *testing.T) {
|
|||||||
map[string]interface{}{"name": "Jane", "age": 25},
|
map[string]interface{}{"name": "Jane", "age": 25},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
success := Set(data, "$.users[0].name", "Bob")
|
err := Set(data, "$.users[0].name", "Bob")
|
||||||
|
if err != nil {
|
||||||
if !success {
|
t.Errorf("Set() returned error: %v", err)
|
||||||
t.Errorf("Set() returned false, expected true")
|
return
|
||||||
}
|
}
|
||||||
users, ok := data["users"].([]interface{})
|
users, ok := data["users"].([]interface{})
|
||||||
if !ok {
|
if !ok {
|
||||||
@@ -219,11 +229,12 @@ func TestSet(t *testing.T) {
|
|||||||
"phone": "123-456-7890",
|
"phone": "123-456-7890",
|
||||||
}
|
}
|
||||||
|
|
||||||
success := Set(data, "$.user.profile", newProfile)
|
err := Set(data, "$.user.profile", newProfile)
|
||||||
|
if err != nil {
|
||||||
if !success {
|
t.Errorf("Set() returned error: %v", err)
|
||||||
t.Errorf("Set() returned false, expected true")
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
userMap, ok := data["user"].(map[string]interface{})
|
userMap, ok := data["user"].(map[string]interface{})
|
||||||
if !ok {
|
if !ok {
|
||||||
t.Fatalf("User is not a map")
|
t.Fatalf("User is not a map")
|
||||||
@@ -246,10 +257,10 @@ func TestSet(t *testing.T) {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
success := Set(data, "$.user.email", "john@example.com")
|
err := Set(data, "$.user.email", "john@example.com")
|
||||||
|
if err != nil {
|
||||||
if !success {
|
t.Errorf("Set() returned error: %v", err)
|
||||||
t.Errorf("Set() returned false, expected true")
|
return
|
||||||
}
|
}
|
||||||
userMap, ok := data["user"].(map[string]interface{})
|
userMap, ok := data["user"].(map[string]interface{})
|
||||||
if !ok {
|
if !ok {
|
||||||
@@ -268,10 +279,10 @@ func TestSet(t *testing.T) {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
success := Set(data, "$.user.contact.email", "john@example.com")
|
err := Set(data, "$.user.contact.email", "john@example.com")
|
||||||
|
if err != nil {
|
||||||
if !success {
|
t.Errorf("Set() returned error: %v", err)
|
||||||
t.Errorf("Set() returned false, expected true")
|
return
|
||||||
}
|
}
|
||||||
userMap, ok := data["user"].(map[string]interface{})
|
userMap, ok := data["user"].(map[string]interface{})
|
||||||
if !ok {
|
if !ok {
|
||||||
@@ -297,11 +308,10 @@ func TestSet(t *testing.T) {
|
|||||||
|
|
||||||
// This should create an empty addresses array, but won't be able to set index 0
|
// This should create an empty addresses array, but won't be able to set index 0
|
||||||
// since the array is empty
|
// since the array is empty
|
||||||
success := Set(data, "$.user.addresses[0].street", "123 Main St")
|
err := Set(data, "$.user.addresses[0].street", "123 Main St")
|
||||||
|
if err != nil {
|
||||||
// This shouldn't succeed because we can't create array elements that don't exist
|
t.Errorf("Set() returned error: %v", err)
|
||||||
if success {
|
return
|
||||||
t.Errorf("Set() returned true, expected false for out-of-bounds array index")
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -313,10 +323,10 @@ func TestSet(t *testing.T) {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
success := Set(data, "$.users[*].active", false)
|
err := Set(data, "$.users[*].active", false)
|
||||||
|
if err != nil {
|
||||||
if !success {
|
t.Errorf("Set() returned error: %v", err)
|
||||||
t.Errorf("Set() returned false, expected true")
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
users, ok := data["users"].([]interface{})
|
users, ok := data["users"].([]interface{})
|
||||||
@@ -350,10 +360,10 @@ func TestSet(t *testing.T) {
|
|||||||
"name": "John",
|
"name": "John",
|
||||||
}
|
}
|
||||||
|
|
||||||
success := Set(data, "$", "Jane")
|
err := Set(data, "$", "Jane")
|
||||||
|
if err == nil {
|
||||||
if success {
|
t.Errorf("Set() returned no error, expected error for setting on root")
|
||||||
t.Errorf("Set() returned true, expected false for setting on root")
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Data should be unchanged
|
// Data should be unchanged
|
||||||
@@ -369,10 +379,10 @@ func TestSetAll(t *testing.T) {
|
|||||||
"name": "John",
|
"name": "John",
|
||||||
"age": 30,
|
"age": 30,
|
||||||
}
|
}
|
||||||
success := SetAll(data, "$.name", "Jane")
|
err := SetAll(data, "$.name", "Jane")
|
||||||
|
if err != nil {
|
||||||
if !success {
|
t.Errorf("SetAll() returned error: %v", err)
|
||||||
t.Errorf("SetAll() returned false, expected true")
|
return
|
||||||
}
|
}
|
||||||
if data["name"] != "Jane" {
|
if data["name"] != "Jane" {
|
||||||
t.Errorf("SetAll() failed: expected name to be 'Jane', got %v", data["name"])
|
t.Errorf("SetAll() failed: expected name to be 'Jane', got %v", data["name"])
|
||||||
@@ -387,10 +397,10 @@ func TestSetAll(t *testing.T) {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
success := SetAll(data, "$.users[*].active", false)
|
err := SetAll(data, "$.users[*].active", false)
|
||||||
|
if err != nil {
|
||||||
if !success {
|
t.Errorf("SetAll() returned error: %v", err)
|
||||||
t.Errorf("SetAll() returned false, expected true")
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
users, ok := data["users"].([]interface{})
|
users, ok := data["users"].([]interface{})
|
||||||
@@ -425,10 +435,10 @@ func TestSetAll(t *testing.T) {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
success := SetAll(data, "$..active", false)
|
err := SetAll(data, "$..active", false)
|
||||||
|
if err != nil {
|
||||||
if !success {
|
t.Errorf("SetAll() returned error: %v", err)
|
||||||
t.Errorf("SetAll() returned false, expected true")
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check user profile
|
// Check user profile
|
||||||
@@ -532,7 +542,11 @@ func TestGetWithPathsExtended(t *testing.T) {
|
|||||||
|
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
result := Get(tt.data, tt.path)
|
result, err := Get(tt.data, tt.path)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("GetWithPaths() returned error: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// Check if lengths match
|
// Check if lengths match
|
||||||
if len(result) != len(tt.expected) {
|
if len(result) != len(tt.expected) {
|
||||||
|
@@ -93,6 +93,7 @@ func TestEvaluator(t *testing.T) {
|
|||||||
name string
|
name string
|
||||||
path string
|
path string
|
||||||
expected []JSONNode
|
expected []JSONNode
|
||||||
|
error bool
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "simple_property_access",
|
name: "simple_property_access",
|
||||||
@@ -146,18 +147,26 @@ func TestEvaluator(t *testing.T) {
|
|||||||
name: "invalid_index",
|
name: "invalid_index",
|
||||||
path: "$.store.book[5]",
|
path: "$.store.book[5]",
|
||||||
expected: []JSONNode{},
|
expected: []JSONNode{},
|
||||||
|
error: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "nonexistent_property",
|
name: "nonexistent_property",
|
||||||
path: "$.store.nonexistent",
|
path: "$.store.nonexistent",
|
||||||
expected: []JSONNode{},
|
expected: []JSONNode{},
|
||||||
|
error: true,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
// Use GetWithPaths directly
|
// Use GetWithPaths directly
|
||||||
result := Get(testData, tt.path)
|
result, err := Get(testData, tt.path)
|
||||||
|
if err != nil {
|
||||||
|
if !tt.error {
|
||||||
|
t.Errorf("Get() returned error: %v", err)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// Special handling for wildcard recursive test
|
// Special handling for wildcard recursive test
|
||||||
if tt.name == "wildcard_recursive" {
|
if tt.name == "wildcard_recursive" {
|
||||||
@@ -201,7 +210,11 @@ func TestEvaluator(t *testing.T) {
|
|||||||
|
|
||||||
func TestEdgeCases(t *testing.T) {
|
func TestEdgeCases(t *testing.T) {
|
||||||
t.Run("empty_data", func(t *testing.T) {
|
t.Run("empty_data", func(t *testing.T) {
|
||||||
result := Get(nil, "$.a.b")
|
result, err := Get(nil, "$.a.b")
|
||||||
|
if err == nil {
|
||||||
|
t.Errorf("Expected error for empty data")
|
||||||
|
return
|
||||||
|
}
|
||||||
if len(result) > 0 {
|
if len(result) > 0 {
|
||||||
t.Errorf("Expected empty result, got %v", result)
|
t.Errorf("Expected empty result, got %v", result)
|
||||||
}
|
}
|
||||||
@@ -218,7 +231,11 @@ func TestEdgeCases(t *testing.T) {
|
|||||||
data := map[string]interface{}{
|
data := map[string]interface{}{
|
||||||
"42": "answer",
|
"42": "answer",
|
||||||
}
|
}
|
||||||
result := Get(data, "$.42")
|
result, err := Get(data, "$.42")
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Get() returned error: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
if len(result) == 0 || result[0].Value != "answer" {
|
if len(result) == 0 || result[0].Value != "answer" {
|
||||||
t.Errorf("Expected 'answer', got %v", result)
|
t.Errorf("Expected 'answer', got %v", result)
|
||||||
}
|
}
|
||||||
@@ -266,7 +283,11 @@ func TestGetWithPaths(t *testing.T) {
|
|||||||
|
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
result := Get(testData, tt.path)
|
result, err := Get(testData, tt.path)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Get() returned error: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// Check if lengths match
|
// Check if lengths match
|
||||||
if len(result) != len(tt.expected) {
|
if len(result) != len(tt.expected) {
|
||||||
|
Reference in New Issue
Block a user