Skip to content

feat: add String() method to Command#12

Merged
wizzomafizzo merged 3 commits into
mainfrom
feat/command-string-method
Apr 11, 2026
Merged

feat: add String() method to Command#12
wizzomafizzo merged 3 commits into
mainfrom
feat/command-string-method

Conversation

@wizzomafizzo
Copy link
Copy Markdown
Member

@wizzomafizzo wizzomafizzo commented Apr 11, 2026

Summary

  • Adds Command.String() that returns the canonical ZapScript representation
  • Handles quoting only when args contain special characters (commas, colons, etc.)
  • Re-escapes control characters (\n^n, \t^t, ^^^)
  • Reconstructs input macro commands (input.keyboard, input.gamepad) in concatenated format
  • Round-trip tested: parse → String() → parse produces equivalent Command

Summary by CodeRabbit

  • Tests

    • Added comprehensive tests validating command text formatting and parse/string/parse round-trips to ensure consistent serialization across edge cases.
  • Chores

    • Implemented canonical command serialization with deterministic ordering of advanced arguments and robust escaping/quoting for special characters and input-macro argument sequences.

Returns the canonical ZapScript representation of a command,
enabling round-trip parse → String() → parse. Handles quoting
for args containing special characters, re-escapes control
characters, and reconstructs input macro commands in their
native concatenated format.

Needed by zaparoo-core for ZapScript-aware allow_run matching.
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 11, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 858075c7-f45e-4a34-9236-0f41398bd6e8

📥 Commits

Reviewing files that changed from the base of the PR and between 797afaf and 3747e8b.

📒 Files selected for processing (1)
  • command_string_test.go
✅ Files skipped from review due to trivial changes (1)
  • command_string_test.go

📝 Walkthrough

Walkthrough

Adds a canonical ZapScript command serializer (Command.String) with escaping/quoting helpers and deterministic AdvArgs ordering, plus a new test file that verifies formatting cases and parse→string→parse round-trip stability.

Changes

Cohort / File(s) Summary
Reader Implementation
reader.go
Added func (c Command) String() string, helper functions argNeedsQuoting(string) bool and escapeArg(string) string, and imports sort, strings. Serializes command name, positional args (special-case for input-macro commands), and deterministic, sorted advanced args with conditional quoting/escaping.
Test Suite
command_string_test.go
New tests under zapscript_test: TestCommandString (table-driven cases for plain, comma/colon/quote-containing args, escaped control chars, AdvArgs ordering/omission, and input.keyboard/input.gamepad macro rules) and TestCommandString_RoundTrip (parse → String → parse round-trip validating Name, Args, and AdvArgs.Raw()).

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

Poem

🐰 I nibbled bytes and sewed a line,
Quotes and care in every sign,
Newlines tucked and commas tamed,
Round-trip checked — the hop's proclaimed,
Code neat, I munch and then I dine.

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat: add String() method to Command' is concise, clear, and directly summarizes the main change—the addition of the String() method to the Command type. It matches the primary objective and is verifiable in the changeset.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/command-string-method

Comment @coderabbitai help to get the list of available commands and usage tips.

@codecov
Copy link
Copy Markdown

codecov Bot commented Apr 11, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Nitpick comments (1)
command_string_test.go (1)

112-114: Use cmp.Diff instead of direct equality in table assertions.

Line 112 uses got != tt.want, which gives weaker diagnostics than cmp.Diff.

💡 Proposed fix
-			if got != tt.want {
-				t.Errorf("Command.String() = %q, want %q", got, tt.want)
+			if diff := cmp.Diff(tt.want, got); diff != "" {
+				t.Errorf("Command.String() mismatch (-want +got):\n%s", diff)
 			}

As per coding guidelines: "Use cmp.Diff from google/go-cmp for detailed assertion output".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@command_string_test.go` around lines 112 - 114, Replace the direct equality
assertion in the TestCommandString table test that checks "if got != tt.want"
for Command.String() with a cmp.Diff-based assertion: import
"github.com/google/go-cmp/cmp", compute diff := cmp.Diff(tt.want, got) and if
diff != "" call t.Errorf with a message that includes the diff (e.g.,
"Command.String() mismatch (-want +got):\n%s", diff); update the test function
(TestCommandString) to use cmp.Diff and add the import to the test file.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@command_string_test.go`:
- Around line 29-33: The test-case struct in the tests slice causes a
fieldalignment vet failure; reorder the struct fields so the larger type
zapscript.Command (cmd) comes before the smaller string fields to satisfy
alignment (e.g., define fields as cmd zapscript.Command, want string, name
string), update any composite literals accordingly in command_string_test.go
(the tests variable and its entries), then run the project's lint-fix task to
ensure formatting and lint checks pass.
- Around line 164-165: The round-trip comparison currently suppresses
differences by using cmpopts.IgnoreUnexported(zapscript.AdvArgs{}); remove that
option from the cmp.Diff invocation comparing script1.Cmds[0] and
script2.Cmds[0], and instead explicitly compare AdvArgs via their public Raw()
values (e.g., verify script1.Cmds[0].AdvArgs.Raw() ==
script2.Cmds[0].AdvArgs.Raw() or include that Raw() field in the cmp.Diff input)
so internal differences in zapscript.AdvArgs are observed.

In `@reader.go`:
- Around line 180-183: Advanced-arg values are written raw (using
b.WriteString(value) after writing key and SymAdvArgEq) but must be
escaped/quoted like regular args; update the serialization to call
argNeedsQuoting(value) and, if true, write the escaped/quoted form via
escapeArg(value) instead of the raw value so round-trip parsing
(parseQuotedArg/parseEscapeSeq) succeeds. Locate the block that writes key,
SymAdvArgEq, and value (the b.WriteString(string(key));
b.WriteRune(SymAdvArgEq); b.WriteString(value); return true sequence) and
replace the raw write with logic that checks argNeedsQuoting(value) and uses
escapeArg(value) when needed.
- Around line 172-185: The current code uses c.AdvArgs.Range to emit
advanced-arg pairs which is non-deterministic; fix by collecting entries during
Range into a slice (or map + keys slice), sort the keys deterministically (e.g.,
with sort.Strings on string(key)), then iterate the sorted keys to write
key/value pairs in Command.String() (referencing c.AdvArgs, IsEmpty, Range and
the String-building code around SymAdvArgStart/SymAdvArgSep/SymAdvArgEq) so the
output is canonical and repeatable.

---

Nitpick comments:
In `@command_string_test.go`:
- Around line 112-114: Replace the direct equality assertion in the
TestCommandString table test that checks "if got != tt.want" for
Command.String() with a cmp.Diff-based assertion: import
"github.com/google/go-cmp/cmp", compute diff := cmp.Diff(tt.want, got) and if
diff != "" call t.Errorf with a message that includes the diff (e.g.,
"Command.String() mismatch (-want +got):\n%s", diff); update the test function
(TestCommandString) to use cmp.Diff and add the import to the test file.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 977c657d-03c7-4b21-8108-9ba00d6f4f89

📥 Commits

Reviewing files that changed from the base of the PR and between a730494 and a73e64e.

📒 Files selected for processing (2)
  • command_string_test.go
  • reader.go

Comment thread command_string_test.go
Comment thread command_string_test.go Outdated
Comment thread reader.go
Comment thread reader.go Outdated
- Sort adv arg keys for deterministic output
- Escape adv arg values that need quoting
- Fix fieldalignment in test struct
- Use _, _ = pattern for strings.Builder writes
- Use cmp.Diff in test assertions
- Compare AdvArgs via Raw() instead of cmpopts.IgnoreUnexported
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
command_string_test.go (1)

130-184: Trim or rewrite the inline comments to explain intent, not mechanics.

Comments like “Parse the original input”, “Convert to string”, and “Compare Name and Args” restate the next line. Prefer brief rationale-level comments only.

As per coding guidelines: "Do not write comments that restate what the code does - comments should explain why, not what".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@command_string_test.go` around lines 130 - 184, The inline comments that
restate code mechanics (e.g., the comments immediately before
reader1.ParseScript(), str := script1.Cmds[0].String(), reader2.ParseScript(),
the cmp.Diff checks for Name/Args, and the "Compare AdvArgs via public Raw()
accessor" line) should be removed or rewritten as short rationale-level comments
explaining why the round-trip check exists (e.g., "ensure parse→String()→parse
round-trips without losing command semantics") and any non-obvious intent (e.g.,
why we require exactly one command). Keep all code and assertions (inputs,
t.Run, NewParser, ParseScript, String(), cmp.Diff, Raw()) unchanged; only
replace or delete the redundant what-level comments so remaining comments convey
intent, not mechanics.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@command_string_test.go`:
- Around line 28-114: The test table for zapscript.Command.String() is missing
edge cases: add cases exercising args that contain a double-quote (`"`), a
carriage return (`\r`), and advanced-arg values that require quoting/escaping
(e.g., values with comma, colon, newline, tab, or caret) so
serialization/escaping logic is validated; update the tests slice in
command_string_test.go to include zapscript.Command entries that assert expected
String() outputs for these inputs and also include AdvArgs created via
zapscript.NewAdvArgs(map[string]string{...}) where a value requires
quoting/escaping, referencing Command.String(), zapscript.Command, and
zapscript.NewAdvArgs to locate the code paths to cover.

---

Nitpick comments:
In `@command_string_test.go`:
- Around line 130-184: The inline comments that restate code mechanics (e.g.,
the comments immediately before reader1.ParseScript(), str :=
script1.Cmds[0].String(), reader2.ParseScript(), the cmp.Diff checks for
Name/Args, and the "Compare AdvArgs via public Raw() accessor" line) should be
removed or rewritten as short rationale-level comments explaining why the
round-trip check exists (e.g., "ensure parse→String()→parse round-trips without
losing command semantics") and any non-obvious intent (e.g., why we require
exactly one command). Keep all code and assertions (inputs, t.Run, NewParser,
ParseScript, String(), cmp.Diff, Raw()) unchanged; only replace or delete the
redundant what-level comments so remaining comments convey intent, not
mechanics.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 3bcd79b1-81e3-40f9-8f3c-2f5e8e84a624

📥 Commits

Reviewing files that changed from the base of the PR and between a73e64e and 797afaf.

📒 Files selected for processing (2)
  • command_string_test.go
  • reader.go
🚧 Files skipped from review as they are similar to previous changes (1)
  • reader.go

Comment thread command_string_test.go
- Add tests for double quote, carriage return in args
- Add tests for adv arg values requiring quoting (comma, colon, newline, tab, caret)
- Remove redundant what-level comments in round-trip test
@wizzomafizzo wizzomafizzo merged commit 922d4e0 into main Apr 11, 2026
13 checks passed
@wizzomafizzo wizzomafizzo deleted the feat/command-string-method branch April 11, 2026 02:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant