big tings
This commit is contained in:
8
parser/utils/math.go
Normal file
8
parser/utils/math.go
Normal file
@@ -0,0 +1,8 @@
|
||||
package utils
|
||||
|
||||
func Abs(x int) int {
|
||||
if x < 0 {
|
||||
return -x
|
||||
}
|
||||
return x
|
||||
}
|
16
parser/utils/read_test_data.go
Normal file
16
parser/utils/read_test_data.go
Normal file
@@ -0,0 +1,16 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func ReadTestDataFile(t *testing.T, fileName string) string {
|
||||
path := filepath.Join("../testdata", fileName)
|
||||
data, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to read test data file: %v", err)
|
||||
}
|
||||
return string(data)
|
||||
}
|
65
parser/utils/set_path_value.go
Normal file
65
parser/utils/set_path_value.go
Normal file
@@ -0,0 +1,65 @@
|
||||
// Package utils contains utility functions for the parser package.
|
||||
package utils
|
||||
|
||||
import (
|
||||
"maps"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// SetPathValue sets value at a dot-separated path in obj, creating maps as needed.
|
||||
// If the path is only dots (e.g., "." or ".."):
|
||||
// - when obj == nil -> returns value directly (e.g., "flour")
|
||||
// - when obj is a map -> if value is a map[string]any, merge into obj; otherwise obj is unchanged.
|
||||
// - otherwise -> returns obj unchanged.
|
||||
func SetPathValue(path string, value any, obj any) any {
|
||||
// Split and drop empty segments (so ".", "..", "" become no keys)
|
||||
raw := strings.Split(path, ".")
|
||||
keys := raw[:0]
|
||||
for _, k := range raw {
|
||||
if k != "" {
|
||||
keys = append(keys, k)
|
||||
}
|
||||
}
|
||||
|
||||
// Root case: no keys after trimming dots
|
||||
if len(keys) == 0 {
|
||||
if obj == nil {
|
||||
return value
|
||||
}
|
||||
if m, ok := obj.(map[string]any); ok {
|
||||
if mv, ok := value.(map[string]any); ok {
|
||||
maps.Copy(m, mv)
|
||||
}
|
||||
return m
|
||||
}
|
||||
return obj
|
||||
}
|
||||
|
||||
// Ensure root is a map
|
||||
var root map[string]any
|
||||
if obj == nil {
|
||||
root = map[string]any{}
|
||||
} else if m, ok := obj.(map[string]any); ok {
|
||||
root = m
|
||||
} else {
|
||||
// if obj is not a map, just overwrite it with a new map
|
||||
root = map[string]any{}
|
||||
}
|
||||
|
||||
// Descend/construct maps
|
||||
curr := root
|
||||
for i, k := range keys {
|
||||
if i == len(keys)-1 {
|
||||
curr[k] = value
|
||||
break
|
||||
}
|
||||
if next, ok := curr[k].(map[string]any); ok {
|
||||
curr = next
|
||||
continue
|
||||
}
|
||||
n := map[string]any{}
|
||||
curr[k] = n
|
||||
curr = n
|
||||
}
|
||||
return root
|
||||
}
|
132
parser/utils/set_path_value_test.go
Normal file
132
parser/utils/set_path_value_test.go
Normal file
@@ -0,0 +1,132 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestSetPathValue_SingleKey(t *testing.T) {
|
||||
input := map[string]any{}
|
||||
got := SetPathValue("name", "Max", input)
|
||||
|
||||
want := map[string]any{"name": "Max"}
|
||||
|
||||
if !reflect.DeepEqual(got, want) {
|
||||
t.Fatalf("unexpected map.\n got: %#v\nwant: %#v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSetPathValue_DotSyntax(t *testing.T) {
|
||||
input := map[string]any{}
|
||||
meta := map[string]any{
|
||||
"name": "Max",
|
||||
}
|
||||
got := SetPathValue(".", meta, input)
|
||||
|
||||
want := map[string]any{"name": "Max"}
|
||||
|
||||
fmt.Printf("%+v\n", got)
|
||||
|
||||
if !reflect.DeepEqual(got, want) {
|
||||
t.Fatalf("unexpected map.\n got: %#v\nwant: %#v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSetPathValue_DotSyntaxString(t *testing.T) {
|
||||
var input any
|
||||
meta := "flour"
|
||||
|
||||
got := SetPathValue(".", meta, input)
|
||||
|
||||
want := "flour"
|
||||
|
||||
fmt.Printf("%+v\n", got)
|
||||
|
||||
if !reflect.DeepEqual(got, want) {
|
||||
t.Fatalf("unexpected map.\n got: %#v\nwant: %#v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSetPathValue_NestedKeys_CreateMissingMaps(t *testing.T) {
|
||||
input := map[string]any{}
|
||||
got := SetPathValue("user.profile.name", "Max", input)
|
||||
|
||||
// Desired behavior: create nested maps and set the value.
|
||||
// NOTE: If this test fails, your implementation likely isn't descending into nested maps.
|
||||
want := map[string]any{
|
||||
"user": map[string]any{
|
||||
"profile": map[string]any{
|
||||
"name": "Max",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(got, want) {
|
||||
t.Fatalf("unexpected map for nested keys.\n got: %#v\nwant: %#v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSetPathValue_OverwriteExistingValue(t *testing.T) {
|
||||
input := map[string]any{"foo": "old"}
|
||||
got := SetPathValue("foo", "new", input)
|
||||
|
||||
want := map[string]any{"foo": "new"}
|
||||
|
||||
if !reflect.DeepEqual(got, want) {
|
||||
t.Fatalf("unexpected map after overwrite.\n got: %#v\nwant: %#v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSetPathValue_PartiallyExistingPath(t *testing.T) {
|
||||
input := map[string]any{
|
||||
"user": map[string]any{
|
||||
"profile": map[string]any{},
|
||||
},
|
||||
}
|
||||
got := SetPathValue("user.profile.age", 28, input)
|
||||
|
||||
want := map[string]any{
|
||||
"user": map[string]any{
|
||||
"profile": map[string]any{
|
||||
"age": 28,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(got, want) {
|
||||
t.Fatalf("unexpected map with partially existing path.\n got: %#v\nwant: %#v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSetPathValue_EmptySegmentsAreIgnored(t *testing.T) {
|
||||
input := map[string]any{}
|
||||
got := SetPathValue("a..b", 1, input)
|
||||
|
||||
// Expected behavior (common-sense): treat empty segments as no-op and still set a.b = 1
|
||||
want := map[string]any{
|
||||
"a": map[string]any{
|
||||
"b": 1,
|
||||
},
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(got, want) {
|
||||
t.Fatalf("unexpected map with empty segments.\n got: %#v\nwant: %#v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSetPathValue_ComplexValueTypes(t *testing.T) {
|
||||
input := map[string]any{}
|
||||
val := []int{1, 2, 3}
|
||||
got := SetPathValue("nums.list", val, input)
|
||||
|
||||
want := map[string]any{
|
||||
"nums": map[string]any{
|
||||
"list": []int{1, 2, 3},
|
||||
},
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(got, want) {
|
||||
t.Fatalf("unexpected map with complex value types.\n got: %#v\nwant: %#v", got, want)
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user