diff --git a/internal/topdata/convert.go b/internal/topdata/convert.go index 2196c51..8f1f8bf 100644 --- a/internal/topdata/convert.go +++ b/internal/topdata/convert.go @@ -107,6 +107,7 @@ func printConvertUsage(stdout io.Writer) { _, _ = fmt.Fprintln(stdout, "2da-to-module notes:") _, _ = fmt.Fprintln(stdout, " - --namespace is optional when topdata/templates/config.json declares it for the input table") _, _ = fmt.Fprintln(stdout, " - --name controls inferred output naming: add__.json") + _, _ = fmt.Fprintln(stdout, " - flags accept both --flag value and --flag=value forms") _, _ = fmt.Fprintln(stdout, " - unkeyed rows now fail conversion instead of being skipped") } @@ -164,6 +165,40 @@ func parseConvertArgs(args []string, allowFormat bool, defaultCollision string) } opts.Type = args[index] default: + if value, ok := parseInlineFlagValue(arg, "--namespace"); ok { + opts.Namespace = value + continue + } + if value, ok := parseInlineFlagValue(arg, "--key-field"); ok { + opts.KeyFields = append(opts.KeyFields, value) + continue + } + if value, ok := parseInlineFlagValue(arg, "--name"); ok { + opts.Name = value + continue + } + if value, ok := parseInlineFlagValue(arg, "--collision"); ok { + opts.CollisionMode = value + continue + } + if value, ok := parseInlineFlagValue(arg, "--base-dialog"); ok { + opts.BaseDialog = value + continue + } + if value, ok := parseInlineFlagValue(arg, "--format"); ok { + if !allowFormat { + return opts, "", "", fmt.Errorf("%s does not accept %s", positionalSafe(args), "--format") + } + opts.Type = value + continue + } + if value, ok := parseInlineFlagValue(arg, "--type"); ok { + if !allowFormat { + return opts, "", "", fmt.Errorf("%s does not accept %s", positionalSafe(args), "--type") + } + opts.Type = value + continue + } if strings.HasPrefix(arg, "--") { return opts, "", "", fmt.Errorf("unknown flag %q", arg) } @@ -243,6 +278,14 @@ func parseConvertArgs(args []string, allowFormat bool, defaultCollision string) return opts, positional[0], positional[1], nil } +func parseInlineFlagValue(arg, name string) (string, bool) { + prefix := name + "=" + if !strings.HasPrefix(arg, prefix) { + return "", false + } + return strings.TrimSpace(arg[len(prefix):]), true +} + func positionalSafe(args []string) string { if len(args) == 0 { return "command" diff --git a/internal/topdata/convert_test.go b/internal/topdata/convert_test.go index d65597d..3080e3b 100644 --- a/internal/topdata/convert_test.go +++ b/internal/topdata/convert_test.go @@ -287,6 +287,39 @@ func TestRunConvertCommand2DAToJSONSuffixesDuplicateGeneratedKeys(t *testing.T) } } +func TestParseConvertArgsSupportsEqualsSyntax(t *testing.T) { + opts, input, output, err := parseConvertArgs([]string{ + "--namespace=environment", + "--name=projectq", + "--type=entries", + "--collision=suffix", + "--key-field=Label", + "input.2da", + "output.json", + }, true, "error") + if err != nil { + t.Fatalf("parseConvertArgs failed: %v", err) + } + if opts.Namespace != "environment" { + t.Fatalf("expected namespace from equals syntax, got %q", opts.Namespace) + } + if opts.Name != "projectq" { + t.Fatalf("expected name from equals syntax, got %q", opts.Name) + } + if opts.Type != "entries" { + t.Fatalf("expected type from equals syntax, got %q", opts.Type) + } + if opts.CollisionMode != "suffix" { + t.Fatalf("expected collision from equals syntax, got %q", opts.CollisionMode) + } + if len(opts.KeyFields) != 1 || opts.KeyFields[0] != "Label" { + t.Fatalf("expected key field from equals syntax, got %#v", opts.KeyFields) + } + if input != "input.2da" || output != "output.json" { + t.Fatalf("unexpected positional parse result: input=%q output=%q", input, output) + } +} + func TestConvert2DAToJSONSkipsConfiguredKeysWhenAnyKeyFieldIsNull(t *testing.T) { result, err := convert2DAToJSON(parsed2DA{ Columns: []string{"LABEL", "RESREF", "STRREF"},