diff --git a/internal/topdata/convert.go b/internal/topdata/convert.go index d3bca09..502a20c 100644 --- a/internal/topdata/convert.go +++ b/internal/topdata/convert.go @@ -215,6 +215,16 @@ func parseConvertArgs(args []string, allowFormat bool) (convertOptions, string, } return opts, positional[0], output, nil } + ctx, err := resolveConvertContext(positional[0]) + if err != nil { + return opts, "", "", err + } + if strings.TrimSpace(opts.Namespace) == "" { + opts.Namespace = ctx.Template.Namespace + } + if len(opts.KeyFields) == 0 && len(ctx.Template.KeyFields) > 0 { + opts.KeyFields = append(opts.KeyFields, ctx.Template.KeyFields...) + } return opts, positional[0], positional[1], nil } diff --git a/internal/topdata/convert_test.go b/internal/topdata/convert_test.go index a0fcb60..d85a864 100644 --- a/internal/topdata/convert_test.go +++ b/internal/topdata/convert_test.go @@ -2,6 +2,7 @@ package topdata import ( "bytes" + "encoding/json" "os" "path/filepath" "strings" @@ -61,6 +62,63 @@ func TestRunConvertCommandInfersTemplateNamespaceAndOutput(t *testing.T) { } } +func TestRunConvertCommand2DAToJSONInfersTemplateKeys(t *testing.T) { + root := testProjectRoot(t) + writeFile(t, filepath.Join(root, "nwn-tool.json"), `{ + "module": {"name": "Test", "resref": "test"}, + "paths": {"source": "src", "assets": "assets", "build": "build"}, + "topdata": {"source": "topdata", "build": "build/topdata"} +}`+"\n") + mkdirAll(t, filepath.Join(root, "topdata", "templates")) + writeFile(t, filepath.Join(root, "topdata", "templates", "config.json"), `{ + "tables": { + "template_appearance": { + "namespace": "appearance", + "key_fields": ["LABEL"] + } + } +}`+"\n") + inputPath := filepath.Join(root, "topdata", "templates", "template_appearance.2da") + writeFile(t, inputPath, "2DA V2.0\n\nLABEL STRING_REF NAME\n960 \"Zombie, Ash, Hot\" **** Zombie_Ash_Hot\n961 \"Zombie, Ash, Done\" **** Zombie_Ash_Done\n") + + oldCwd, err := os.Getwd() + if err != nil { + t.Fatalf("getwd: %v", err) + } + t.Cleanup(func() { + _ = os.Chdir(oldCwd) + }) + if err := os.Chdir(root); err != nil { + t.Fatalf("chdir: %v", err) + } + + outputPath := filepath.Join(root, "out", "appearance.json") + if err := RunConvertCommand([]string{ + "2da-to-json", + "topdata/templates/template_appearance.2da", + outputPath, + }, &bytes.Buffer{}); err != nil { + t.Fatalf("RunConvertCommand failed: %v", err) + } + + raw, err := os.ReadFile(outputPath) + if err != nil { + t.Fatalf("read output: %v", err) + } + var payload struct { + Rows []map[string]any `json:"rows"` + } + if err := json.Unmarshal(raw, &payload); err != nil { + t.Fatalf("unmarshal output: %v", err) + } + if got := payload.Rows[0]["key"]; got != "appearance:zombie_ash_hot" { + t.Fatalf("expected first key to be inferred from template config, got %#v", got) + } + if got := payload.Rows[1]["key"]; got != "appearance:zombie_ash_done" { + t.Fatalf("expected second key to be inferred from template config, got %#v", got) + } +} + func TestRunConvertCommandFailsOnUnkeyedRows(t *testing.T) { root := testProjectRoot(t) writeFile(t, filepath.Join(root, "nwn-tool.json"), `{