Compare commits
6 Commits
bbc7c50fae
...
master
Author | SHA1 | Date | |
---|---|---|---|
969ccae25c | |||
5b46ff0efd | |||
d234616406 | |||
af3e55e518 | |||
13b48229ac | |||
670f6ed7a0 |
13
.vscode/launch.json
vendored
13
.vscode/launch.json
vendored
@@ -98,6 +98,19 @@
|
||||
"args": [
|
||||
"cook_tacz.yml",
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Launch Package (ICARUS)",
|
||||
"type": "go",
|
||||
"request": "launch",
|
||||
"mode": "auto",
|
||||
"program": "${workspaceFolder}",
|
||||
"cwd": "C:/Users/Administrator/Seafile/Games-ICARUS/Icarus/Saved/IME3/Mods",
|
||||
"args": [
|
||||
"-loglevel",
|
||||
"trace",
|
||||
"cook_processorrecipes.yml",
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
9
go.mod
9
go.mod
@@ -13,7 +13,6 @@ require (
|
||||
|
||||
require (
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/google/go-cmp v0.6.0 // indirect
|
||||
github.com/hexops/valast v1.5.0 // indirect
|
||||
github.com/jinzhu/inflection v1.0.0 // indirect
|
||||
github.com/jinzhu/now v1.1.5 // indirect
|
||||
@@ -21,10 +20,8 @@ require (
|
||||
github.com/mattn/go-sqlite3 v1.14.22 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/rogpeppe/go-internal v1.14.1 // indirect
|
||||
github.com/tidwall/gjson v1.18.0 // indirect
|
||||
github.com/tidwall/match v1.1.1 // indirect
|
||||
github.com/tidwall/pretty v1.2.0 // indirect
|
||||
github.com/tidwall/sjson v1.2.5 // indirect
|
||||
golang.org/x/mod v0.21.0 // indirect
|
||||
golang.org/x/sync v0.11.0 // indirect
|
||||
golang.org/x/text v0.22.0 // indirect
|
||||
@@ -33,4 +30,8 @@ require (
|
||||
mvdan.cc/gofumpt v0.4.0 // indirect
|
||||
)
|
||||
|
||||
require gorm.io/driver/sqlite v1.6.0
|
||||
require (
|
||||
github.com/google/go-cmp v0.6.0
|
||||
github.com/tidwall/gjson v1.18.0
|
||||
gorm.io/driver/sqlite v1.6.0
|
||||
)
|
||||
|
3
go.sum
3
go.sum
@@ -36,15 +36,12 @@ github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0t
|
||||
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
|
||||
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/tidwall/gjson v1.14.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
|
||||
github.com/tidwall/gjson v1.18.0 h1:FIDeeyB800efLX89e5a8Y0BNH+LOngJyGrIWxG2FKQY=
|
||||
github.com/tidwall/gjson v1.18.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
|
||||
github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
|
||||
github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
|
||||
github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs=
|
||||
github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
|
||||
github.com/tidwall/sjson v1.2.5 h1:kLy8mja+1c9jlljvWTlSazM7cKDRfJuR/bOJhcY5NcY=
|
||||
github.com/tidwall/sjson v1.2.5/go.mod h1:Fvgq9kS/6ociJEDnK0Fk1cpYF4FIW6ZF7LAe+6jwd28=
|
||||
github.com/yuin/gopher-lua v1.1.1 h1:kYKnWBjvbNP4XLT3+bPEwAXJx262OhaHDWDVOPjL46M=
|
||||
github.com/yuin/gopher-lua v1.1.1/go.mod h1:GBR0iDaNXjAgGg9zfCvksxSRnQx76gclCIb7kdAd1Pw=
|
||||
golang.org/x/mod v0.21.0 h1:vvrHzRwRfVKSiLrG+d4FMl/Qi4ukBCE6kZlTUkDYRT0=
|
||||
|
@@ -89,10 +89,11 @@ func ProcessJSON(content string, command utils.ModifyCommand, filename string) (
|
||||
return commands, fmt.Errorf("failed to convert Lua table back to Go: %v", err)
|
||||
}
|
||||
|
||||
commands, err = applyJSONChanges(content, jsonData, goData)
|
||||
processJsonLogger.Debug("About to call applyChanges with original data and modified data")
|
||||
commands, err = applyChanges(content, jsonData, goData)
|
||||
if err != nil {
|
||||
processJsonLogger.Error("Failed to apply JSON changes: %v", err)
|
||||
return commands, fmt.Errorf("failed to apply JSON changes: %v", err)
|
||||
processJsonLogger.Error("Failed to apply surgical JSON changes: %v", err)
|
||||
return commands, fmt.Errorf("failed to apply surgical JSON changes: %v", err)
|
||||
}
|
||||
|
||||
processJsonLogger.Debug("Total JSON processing time: %v", time.Since(startTime))
|
||||
@@ -145,24 +146,23 @@ func applyChanges(content string, originalData, modifiedData interface{}) ([]uti
|
||||
// Apply removals first (from end to beginning to avoid index shifting)
|
||||
for _, removalPath := range removals {
|
||||
actualPath := strings.TrimSuffix(removalPath, "@remove")
|
||||
index := extractIndexFromRemovalPath(removalPath)
|
||||
elementIndex := extractIndexFromRemovalPath(actualPath)
|
||||
arrayPath := getArrayPathFromElementPath(actualPath)
|
||||
|
||||
// Get the array element to remove
|
||||
result := gjson.Get(content, actualPath)
|
||||
if !result.Exists() {
|
||||
continue
|
||||
}
|
||||
jsonLogger.Debug("Processing removal: path=%s, index=%d, arrayPath=%s", actualPath, elementIndex, arrayPath)
|
||||
|
||||
// Find the exact byte range to remove
|
||||
from, to := findArrayElementRemovalRange(content, arrayPath, elementIndex)
|
||||
|
||||
jsonLogger.Debug("Removing bytes %d-%d", from, to)
|
||||
|
||||
// Find the exact byte range to remove (including comma/formatting)
|
||||
startPos, endPos := findArrayElementRemovalRange(content, arrayPath, index)
|
||||
if startPos >= 0 && endPos > startPos {
|
||||
commands = append(commands, utils.ReplaceCommand{
|
||||
From: startPos,
|
||||
To: endPos,
|
||||
With: "", // Remove the element
|
||||
From: from,
|
||||
To: to,
|
||||
With: "",
|
||||
})
|
||||
}
|
||||
|
||||
jsonLogger.Debug("Added removal command: From=%d, To=%d, With=\"\"", from, to)
|
||||
}
|
||||
|
||||
// Apply additions (new fields)
|
||||
@@ -200,10 +200,11 @@ func applyChanges(content string, originalData, modifiedData interface{}) ([]uti
|
||||
// Convert the new value to JSON string
|
||||
newValueStr := convertValueToJSONString(newValue)
|
||||
|
||||
// Insert the new field
|
||||
|
||||
// Insert the new field with pretty-printed formatting
|
||||
// Format: ,"fieldName": { ... }
|
||||
insertText := fmt.Sprintf(`,"%s": %s`, fieldName, newValueStr)
|
||||
|
||||
jsonLogger.Debug("Inserting text: %q", insertText)
|
||||
|
||||
commands = append(commands, utils.ReplaceCommand{
|
||||
From: startPos,
|
||||
@@ -311,10 +312,31 @@ func convertValueToJSONString(value interface{}) string {
|
||||
return strconv.FormatBool(v)
|
||||
case nil:
|
||||
return "null"
|
||||
case map[string]interface{}:
|
||||
// Handle maps specially to avoid double-escaping of keys
|
||||
var pairs []string
|
||||
for key, val := range v {
|
||||
// The key might already have escaped quotes from Lua, so we need to be careful
|
||||
// If the key already contains escaped quotes, we need to unescape them first
|
||||
keyStr := key
|
||||
if strings.Contains(key, `\"`) {
|
||||
// Key already has escaped quotes, use it as-is
|
||||
keyStr = `"` + key + `"`
|
||||
} else {
|
||||
// Normal key, escape quotes
|
||||
keyStr = `"` + strings.ReplaceAll(key, `"`, `\"`) + `"`
|
||||
}
|
||||
valStr := convertValueToJSONString(val)
|
||||
pairs = append(pairs, keyStr+":"+valStr)
|
||||
}
|
||||
return "{" + strings.Join(pairs, ",") + "}"
|
||||
default:
|
||||
// For complex types, we need to avoid json.Marshal
|
||||
// This should not happen if we're doing true surgical edits
|
||||
return ""
|
||||
// For other complex types (arrays), we need to use json.Marshal
|
||||
jsonBytes, err := json.Marshal(v)
|
||||
if err != nil {
|
||||
return "null" // Fallback to null if marshaling fails
|
||||
}
|
||||
return string(jsonBytes)
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -247,7 +247,7 @@ modified = false
|
||||
initLuaHelpersLogger.Debug("Setting up Lua print function to Go")
|
||||
L.SetGlobal("print", L.NewFunction(printToGo))
|
||||
L.SetGlobal("fetch", L.NewFunction(fetch))
|
||||
L.SetGlobal("re", L.NewFunction(evalRegex))
|
||||
L.SetGlobal("re", L.NewFunction(EvalRegex))
|
||||
initLuaHelpersLogger.Debug("Lua print and fetch functions bound to Go")
|
||||
return nil
|
||||
}
|
||||
@@ -483,24 +483,39 @@ func fetch(L *lua.LState) int {
|
||||
return 1
|
||||
}
|
||||
|
||||
func evalRegex(L *lua.LState) int {
|
||||
func EvalRegex(L *lua.LState) int {
|
||||
evalRegexLogger := processorLogger.WithPrefix("evalRegex")
|
||||
evalRegexLogger.Debug("Lua evalRegex function called")
|
||||
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
evalRegexLogger.Error("Panic in EvalRegex: %v", r)
|
||||
// Push empty table on panic
|
||||
emptyTable := L.NewTable()
|
||||
L.Push(emptyTable)
|
||||
}
|
||||
}()
|
||||
|
||||
pattern := L.ToString(1)
|
||||
input := L.ToString(2)
|
||||
|
||||
evalRegexLogger.Debug("Pattern: %q, Input: %q", pattern, input)
|
||||
|
||||
re := regexp.MustCompile(pattern)
|
||||
matches := re.FindStringSubmatch(input)
|
||||
|
||||
evalRegexLogger.Debug("Go regex matches: %v (count: %d)", matches, len(matches))
|
||||
|
||||
matchesTable := L.NewTable()
|
||||
for i, match := range matches {
|
||||
matchesTable.RawSetString(fmt.Sprintf("%d", i), lua.LString(match))
|
||||
matchesTable.RawSetInt(i, lua.LString(match))
|
||||
evalRegexLogger.Debug("Set table[%d] = %q", i, match)
|
||||
}
|
||||
|
||||
L.Push(matchesTable)
|
||||
evalRegexLogger.Debug("Pushed matches table to Lua stack")
|
||||
|
||||
return 0
|
||||
return 1
|
||||
}
|
||||
|
||||
// GetLuaFunctionsHelp returns a comprehensive help string for all available Lua functions
|
||||
|
162
processor/processor_test.go
Normal file
162
processor/processor_test.go
Normal file
@@ -0,0 +1,162 @@
|
||||
package processor_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
lua "github.com/yuin/gopher-lua"
|
||||
|
||||
"cook/processor"
|
||||
)
|
||||
|
||||
// Happy Path: Function correctly returns all regex capture groups as Lua table when given valid pattern and input.
|
||||
func TestEvalRegex_CaptureGroupsReturned(t *testing.T) {
|
||||
L := lua.NewState()
|
||||
defer L.Close()
|
||||
pattern := `(\w+)-(\d+)`
|
||||
input := "test-42"
|
||||
L.Push(lua.LString(pattern))
|
||||
L.Push(lua.LString(input))
|
||||
|
||||
result := processor.EvalRegex(L)
|
||||
|
||||
assert.Equal(t, 0, result, "Expected return value to be 0")
|
||||
|
||||
out := L.Get(-1)
|
||||
tbl, ok := out.(*lua.LTable)
|
||||
if !ok {
|
||||
t.Fatalf("Expected Lua table, got %T", out)
|
||||
}
|
||||
expected := []string{"test-42", "test", "42"}
|
||||
for i, v := range expected {
|
||||
val := tbl.RawGetString(fmt.Sprintf("%d", i))
|
||||
assert.Equal(t, lua.LString(v), val, "Expected index %d to be %q", i, v)
|
||||
}
|
||||
}
|
||||
|
||||
// Happy Path: Function returns an empty Lua table when regex pattern does not match input string.
|
||||
func TestEvalRegex_NoMatchReturnsEmptyTable(t *testing.T) {
|
||||
L := lua.NewState()
|
||||
defer L.Close()
|
||||
L.Push(lua.LString(`(foo)(bar)`))
|
||||
L.Push(lua.LString("no-match-here"))
|
||||
|
||||
result := processor.EvalRegex(L)
|
||||
assert.Equal(t, 0, result)
|
||||
|
||||
out := L.Get(-1)
|
||||
tbl, ok := out.(*lua.LTable)
|
||||
if !ok {
|
||||
t.Fatalf("Expected Lua table, got %T", out)
|
||||
}
|
||||
count := 0
|
||||
tbl.ForEach(func(k, v lua.LValue) {
|
||||
count++
|
||||
})
|
||||
assert.Zero(t, count, "Expected no items in the table for non-matching input")
|
||||
}
|
||||
|
||||
// Happy Path: Function handles patterns with no capture groups by returning the full match in the Lua table.
|
||||
func TestEvalRegex_NoCaptureGroups(t *testing.T) {
|
||||
L := lua.NewState()
|
||||
defer L.Close()
|
||||
pattern := `foo\d+`
|
||||
input := "foo123"
|
||||
L.Push(lua.LString(pattern))
|
||||
L.Push(lua.LString(input))
|
||||
|
||||
result := processor.EvalRegex(L)
|
||||
assert.Equal(t, 0, result)
|
||||
|
||||
out := L.Get(-1)
|
||||
tbl, ok := out.(*lua.LTable)
|
||||
if !ok {
|
||||
t.Fatalf("Expected Lua table, got %T", out)
|
||||
}
|
||||
fullMatch := tbl.RawGetString("0")
|
||||
assert.Equal(t, lua.LString("foo123"), fullMatch)
|
||||
// There should be only the full match (index 0)
|
||||
count := 0
|
||||
tbl.ForEach(func(k, v lua.LValue) {
|
||||
count++
|
||||
})
|
||||
assert.Equal(t, 1, count)
|
||||
}
|
||||
|
||||
// Edge Case: Function panics or errors when given an invalid regex pattern.
|
||||
func TestEvalRegex_InvalidPattern(t *testing.T) {
|
||||
L := lua.NewState()
|
||||
defer L.Close()
|
||||
pattern := `([a-z` // invalid regex
|
||||
L.Push(lua.LString(pattern))
|
||||
L.Push(lua.LString("someinput"))
|
||||
|
||||
defer func() {
|
||||
if r := recover(); r == nil {
|
||||
t.Error("Expected panic for invalid regex pattern, but did not panic")
|
||||
}
|
||||
}()
|
||||
processor.EvalRegex(L)
|
||||
}
|
||||
|
||||
// Edge Case: Function returns an empty Lua table when input string is empty.
|
||||
func TestEvalRegex_EmptyInputString(t *testing.T) {
|
||||
L := lua.NewState()
|
||||
defer L.Close()
|
||||
L.Push(lua.LString(`(foo)`))
|
||||
L.Push(lua.LString(""))
|
||||
|
||||
result := processor.EvalRegex(L)
|
||||
assert.Equal(t, 0, result)
|
||||
|
||||
out := L.Get(-1)
|
||||
tbl, ok := out.(*lua.LTable)
|
||||
if !ok {
|
||||
t.Fatalf("Expected Lua table, got %T", out)
|
||||
}
|
||||
// Should be empty
|
||||
count := 0
|
||||
tbl.ForEach(func(k, v lua.LValue) {
|
||||
count++
|
||||
})
|
||||
assert.Zero(t, count, "Expected empty table when input is empty")
|
||||
}
|
||||
|
||||
// Edge Case: Function handles nil or missing arguments gracefully without causing a runtime panic.
|
||||
func TestEvalRegex_MissingArguments(t *testing.T) {
|
||||
L := lua.NewState()
|
||||
defer L.Close()
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
t.Errorf("Did not expect panic when arguments are missing, got: %v", r)
|
||||
}
|
||||
}()
|
||||
// No arguments pushed at all
|
||||
processor.EvalRegex(L)
|
||||
// Should just not match anything or produce empty table, but must not panic
|
||||
}
|
||||
|
||||
func TestEvalComplexRegex(t *testing.T) {
|
||||
// 23:47:35.567068 processor.go:369 [g:22 ] [LUA] Pistol_Round ^((Bulk_)?(Pistol|Rifle).*?Round.*?)$
|
||||
L := lua.NewState()
|
||||
defer L.Close()
|
||||
pattern := `^((Bulk_)?(Pistol|Rifle).*?Round.*?)$`
|
||||
input := "Pistol_Round"
|
||||
L.Push(lua.LString(pattern))
|
||||
L.Push(lua.LString(input))
|
||||
|
||||
processor.EvalRegex(L)
|
||||
|
||||
out := L.Get(-1)
|
||||
tbl, ok := out.(*lua.LTable)
|
||||
if !ok {
|
||||
t.Fatalf("Expected Lua table, got %T", out)
|
||||
}
|
||||
count := 0
|
||||
tbl.ForEach(func(k, v lua.LValue) {
|
||||
fmt.Println(k, v)
|
||||
count++
|
||||
})
|
||||
assert.Equal(t, 1, count)
|
||||
}
|
@@ -3,6 +3,8 @@ package processor
|
||||
import (
|
||||
"cook/utils"
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
)
|
||||
|
||||
func TestSurgicalJSONEditing(t *testing.T) {
|
||||
@@ -92,6 +94,11 @@ modified = true
|
||||
result = result[:cmd.From] + cmd.With + result[cmd.To:]
|
||||
}
|
||||
|
||||
diff := cmp.Diff(result, tt.expected)
|
||||
if diff != "" {
|
||||
t.Errorf("Differences:\n%s", diff)
|
||||
}
|
||||
|
||||
// Check the actual result matches expected
|
||||
if result != tt.expected {
|
||||
t.Errorf("Expected:\n%s\n\nGot:\n%s", tt.expected, result)
|
||||
@@ -178,6 +185,11 @@ modified = true
|
||||
result = result[:cmd.From] + cmd.With + result[cmd.To:]
|
||||
}
|
||||
|
||||
diff := cmp.Diff(result, expected)
|
||||
if diff != "" {
|
||||
t.Errorf("Differences:\n%s", diff)
|
||||
}
|
||||
|
||||
// Check that the result matches expected (preserves formatting and changes weight)
|
||||
if result != expected {
|
||||
t.Errorf("Expected:\n%s\n\nGot:\n%s", expected, result)
|
||||
@@ -500,6 +512,11 @@ func TestSurgicalJSONPreservesFormatting2(t *testing.T) {
|
||||
result = result[:cmd.From] + cmd.With + result[cmd.To:]
|
||||
}
|
||||
|
||||
diff := cmp.Diff(result, expected)
|
||||
if diff != "" {
|
||||
t.Errorf("Differences:\n%s", diff)
|
||||
}
|
||||
|
||||
// Check that the result matches expected (preserves formatting and changes weight)
|
||||
if result != expected {
|
||||
t.Errorf("Expected:\n%s\n\nGot:\n%s", expected, result)
|
||||
@@ -592,8 +609,239 @@ func TestRetardedJSONEditing(t *testing.T) {
|
||||
result = result[:cmd.From] + cmd.With + result[cmd.To:]
|
||||
}
|
||||
|
||||
diff := cmp.Diff(result, expected)
|
||||
if diff != "" {
|
||||
t.Errorf("Differences:\n%s", diff)
|
||||
}
|
||||
|
||||
// Check that the weight was changed
|
||||
if result != expected {
|
||||
t.Errorf("Expected:\n%s\nGot:\n%s", expected, result)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRetardedJSONEditing2(t *testing.T) {
|
||||
original := `
|
||||
{
|
||||
"Rows": [
|
||||
{
|
||||
"Name": "Deep_Mining_Drill_Biofuel",
|
||||
"Meshable": {
|
||||
"RowName": "Mesh_Deep_Mining_Drill_Biofuel"
|
||||
},
|
||||
"Itemable": {
|
||||
"RowName": "Item_Deep_Mining_Drill_Biofuel"
|
||||
},
|
||||
"Interactable": {
|
||||
"RowName": "Deployable"
|
||||
},
|
||||
"Focusable": {
|
||||
"RowName": "Focusable_1H"
|
||||
},
|
||||
"Highlightable": {
|
||||
"RowName": "Generic"
|
||||
},
|
||||
"Actionable": {
|
||||
"RowName": "Deployable"
|
||||
},
|
||||
"Usable": {
|
||||
"RowName": "Place"
|
||||
},
|
||||
"Deployable": {
|
||||
"RowName": "Deep_Mining_Drill_Biofuel"
|
||||
},
|
||||
"Durable": {
|
||||
"RowName": "Deployable_750"
|
||||
},
|
||||
"Inventory": {
|
||||
"RowName": "Deep_Mining_Drill_Biofuel"
|
||||
},
|
||||
"Decayable": {
|
||||
"RowName": "Decay_MetaItem"
|
||||
},
|
||||
"Generator": {
|
||||
"RowName": "Deep_Mining_Biofuel_Drill"
|
||||
},
|
||||
"Resource": {
|
||||
"RowName": "Simple_Internal_Flow_Only"
|
||||
},
|
||||
"Manual_Tags": {
|
||||
"GameplayTags": [
|
||||
{
|
||||
"TagName": "Item.Machine"
|
||||
}
|
||||
]
|
||||
},
|
||||
"Generated_Tags": {
|
||||
"GameplayTags": [
|
||||
{
|
||||
"TagName": "Item.Machine"
|
||||
},
|
||||
{
|
||||
"TagName": "Traits.Meshable"
|
||||
},
|
||||
{
|
||||
"TagName": "Traits.Itemable"
|
||||
},
|
||||
{
|
||||
"TagName": "Traits.Interactable"
|
||||
},
|
||||
{
|
||||
"TagName": "Traits.Highlightable"
|
||||
},
|
||||
{
|
||||
"TagName": "Traits.Actionable"
|
||||
},
|
||||
{
|
||||
"TagName": "Traits.Usable"
|
||||
},
|
||||
{
|
||||
"TagName": "Traits.Deployable"
|
||||
},
|
||||
{
|
||||
"TagName": "Traits.Durable"
|
||||
},
|
||||
{
|
||||
"TagName": "Traits.Inventory"
|
||||
}
|
||||
],
|
||||
"ParentTags": []
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
`
|
||||
|
||||
expected := `
|
||||
{
|
||||
"Rows": [
|
||||
{
|
||||
"Name": "Deep_Mining_Drill_Biofuel",
|
||||
"Meshable": {
|
||||
"RowName": "Mesh_Deep_Mining_Drill_Biofuel"
|
||||
},
|
||||
"Itemable": {
|
||||
"RowName": "Item_Deep_Mining_Drill_Biofuel"
|
||||
},
|
||||
"Interactable": {
|
||||
"RowName": "Deployable"
|
||||
},
|
||||
"Focusable": {
|
||||
"RowName": "Focusable_1H"
|
||||
},
|
||||
"Highlightable": {
|
||||
"RowName": "Generic"
|
||||
},
|
||||
"Actionable": {
|
||||
"RowName": "Deployable"
|
||||
},
|
||||
"Usable": {
|
||||
"RowName": "Place"
|
||||
},
|
||||
"Deployable": {
|
||||
"RowName": "Deep_Mining_Drill_Biofuel"
|
||||
},
|
||||
"Durable": {
|
||||
"RowName": "Deployable_750"
|
||||
},
|
||||
"Inventory": {
|
||||
"RowName": "Deep_Mining_Drill_Biofuel"
|
||||
},
|
||||
"Decayable": {
|
||||
"RowName": "Decay_MetaItem"
|
||||
},
|
||||
"Generator": {
|
||||
"RowName": "Deep_Mining_Biofuel_Drill"
|
||||
},
|
||||
"Resource": {
|
||||
"RowName": "Simple_Internal_Flow_Only"
|
||||
},
|
||||
"Manual_Tags": {
|
||||
"GameplayTags": [
|
||||
{
|
||||
"TagName": "Item.Machine"
|
||||
}
|
||||
]
|
||||
},
|
||||
"Generated_Tags": {
|
||||
"GameplayTags": [
|
||||
{
|
||||
"TagName": "Item.Machine"
|
||||
},
|
||||
{
|
||||
"TagName": "Traits.Meshable"
|
||||
},
|
||||
{
|
||||
"TagName": "Traits.Itemable"
|
||||
},
|
||||
{
|
||||
"TagName": "Traits.Interactable"
|
||||
},
|
||||
{
|
||||
"TagName": "Traits.Highlightable"
|
||||
},
|
||||
{
|
||||
"TagName": "Traits.Actionable"
|
||||
},
|
||||
{
|
||||
"TagName": "Traits.Usable"
|
||||
},
|
||||
{
|
||||
"TagName": "Traits.Deployable"
|
||||
},
|
||||
{
|
||||
"TagName": "Traits.Durable"
|
||||
},
|
||||
{
|
||||
"TagName": "Traits.Inventory"
|
||||
}
|
||||
],
|
||||
"ParentTags": []
|
||||
}
|
||||
,"AdditionalStats": {"(Value=\"BaseDeepMiningDrillSpeed_+%\")":4000}}
|
||||
]
|
||||
}
|
||||
`
|
||||
|
||||
command := utils.ModifyCommand{
|
||||
Name: "test",
|
||||
Lua: `
|
||||
for i, row in ipairs(data.Rows) do
|
||||
-- Special case: Deep_Mining_Drill_Biofuel
|
||||
if string.find(row.Name, "Deep_Mining_Drill_Biofuel") then
|
||||
print("[DEBUG] Special case: Deep_Mining_Drill_Biofuel")
|
||||
if not row.AdditionalStats then
|
||||
print("[DEBUG] Creating AdditionalStats table for Deep_Mining_Drill_Biofuel")
|
||||
row.AdditionalStats = {}
|
||||
end
|
||||
print("[DEBUG] Setting BaseDeepMiningDrillSpeed_+% to 4000")
|
||||
row.AdditionalStats["(Value=\\\"BaseDeepMiningDrillSpeed_+%\\\")"] = 4000
|
||||
end
|
||||
end
|
||||
`,
|
||||
}
|
||||
|
||||
commands, err := ProcessJSON(original, command, "test.json")
|
||||
if err != nil {
|
||||
t.Fatalf("ProcessJSON failed: %v", err)
|
||||
}
|
||||
|
||||
if len(commands) == 0 {
|
||||
t.Fatal("Expected at least one command")
|
||||
}
|
||||
|
||||
// Apply the commands
|
||||
result := original
|
||||
for _, cmd := range commands {
|
||||
result = result[:cmd.From] + cmd.With + result[cmd.To:]
|
||||
}
|
||||
|
||||
diff := cmp.Diff(result, expected)
|
||||
if diff != "" {
|
||||
t.Errorf("Differences:\n%s", diff)
|
||||
}
|
||||
|
||||
if result != expected {
|
||||
t.Errorf("Expected:\n%s\nGot:\n%s", expected, result)
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user