Skip to content

refactor(parser): share plugin-option parser between create and config#10

Merged
ultimatile merged 6 commits into
mainfrom
refactor/issue-9-shared-plugin-option-parser
Apr 26, 2026
Merged

refactor(parser): share plugin-option parser between create and config#10
ultimatile merged 6 commits into
mainfrom
refactor/issue-9-shared-plugin-option-parser

Conversation

@ultimatile

Copy link
Copy Markdown
Owner

Summary

Closes #9. Both CreateCommand and ConfigCommand accept the same KEY=VALUE plugin-option bundle shape, but they previously held independent parsers with divergent rules — CreateCommand's copy did Float-coerce 1.10 to 1.1, ignored quotes, and didn't handle bracket arrays. Inputs that worked under config set silently corrupted under create.

This PR extracts the working ConfigCommand parser into a new PluginOptionParser module that both commands consume.

Changes

  1. refactor(parser) — new module PluginOptionParser (src/plugin_option_parser.jl) hosting parse_value, split_string, parse_kv_string. ConfigCommand's helpers (parse_plugin_option_value, _split_plugin_option_string, _parse_plugin_kv_string) move into it; ConfigCommand and CreateCommand import from it. The broken CreateCommand-local parse_plugin_option_value (Float coercion, no quote handling) is deleted along with the testset that asserted its broken contract. The parse_plugin_option_value contract testset relocates from test_config_command.jl into the new test_plugin_option_parser.jl.
  2. test(create) — three E2E acceptance tests drive real create argv strings through the unified parser to the dry-run plan: quoted-comma values stay single strings (Git.name="Doe, Jane"), bracket arrays become Vector{String} (Git.ignore=[...]), and version-like values stay String (ProjectFile.version=1.10).

Behaviour change

For create: plugin option values now follow the config set parser semantics. Decimals stay String, quoted strings preserve internal commas, bracket arrays round-trip. The config set path is unchanged (same code, new home).

Test plan

  • All existing tests pass (832 → 858, +26)
  • --git 'name="Doe, Jane" email=x'Git.name == "Doe, Jane" end-to-end
  • --git 'ignore=[.DS_Store, .vscode]' → Vector reaches plugin options
  • --projectfile version=1.10 → version stays "1.10", no Float coercion
  • parse_value, split_string, parse_kv_string covered with the 12 edge cases audited during research
  • ConfigCommand behaviour unchanged (existing config tests still pass against the new module)

CreateCommand previously had its own parse_plugin_option_value with
divergent rules (Float coercion of "1.10" → 1.1, no quote handling, no
bracket arrays), so inputs that worked under config set silently
corrupted under create. Move the working ConfigCommand parser into a
new PluginOptionParser module and route both modules through it; the
broken local copy on the create side is deleted. Behaviour for config
set is unchanged (same code, new home).

Also relocate the parse_plugin_option_value contract testset from
test_config_command.jl into the dedicated test_plugin_option_parser.jl
so it lives next to the function it guards. The CreateCommand-local
testset that asserted Float coercion is removed: that contract was the
bug.

Closes #9
Add three E2E testsets that drive real argv strings through the
shared PluginOptionParser into CreateCommand.execute(dry-run):
quoted-comma values stay single strings (Git.name="Doe, Jane"),
bracket arrays become Vector{String} (Git.ignore=[...]), and
version-like values stay strings (ProjectFile.version=1.10) instead
of being coerced to Float and losing the trailing zero. These were
the exact inputs that silently corrupted under create before the
parser unification.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Refactors plugin-option parsing so both create and config set share a single, quote/bracket-aware KEY=VALUE bundle parser, eliminating previously divergent coercion rules (notably the create float-coercion bug for version-like values).

Changes:

  • Added PluginOptionParser module with parse_value, split_string, and parse_kv_string.
  • Updated CreateCommand and ConfigCommand to route plugin option parsing through PluginOptionParser.
  • Reorganized and expanded tests: moved parser contract tests into a dedicated file and added create E2E coverage for the issue #9 acceptance cases.

Reviewed changes

Copilot reviewed 8 out of 8 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
src/plugin_option_parser.jl Introduces the shared parsing implementation used by both commands.
src/create_command.jl Removes the local value parser and uses PluginOptionParser.parse_kv_string for plugin bundles.
src/config_command.jl Drops local helpers and uses PluginOptionParser.parse_kv_string when applying config set plugin options.
src/PkgTemplatesCommandLineInterface.jl Ensures the new module is included before consumers.
test/test_plugin_option_parser.jl New centralized contract tests for parsing behavior.
test/test_create_command.jl Removes tests for the deleted, broken local parser; adds E2E acceptance tests for unified parsing.
test/test_config_command.jl Removes the old parser contract tests that moved to the new shared test file.
test/runtests.jl Adds the new parser test file to the test suite.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/plugin_option_parser.jl Outdated
Comment thread src/plugin_option_parser.jl Outdated
split_string previously only treated a literal space as a separator, so
pasted multiline bundles or tab-separated input failed to split. Use
isspace(c) so the implementation matches the "whitespace" docstring and
covers tabs and newlines.

parse_kv_string previously inserted entries under an empty-string key
when given malformed tokens like "=value" or "  =x". Skip those tokens
so downstream plugin construction and TOML writes never see a blank
option name. Both fixes get explicit contract tests.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 8 out of 8 changed files in this pull request and generated no new comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

split_string previously entered quote mode on any '/" outside an
existing quote/bracket context, so name=O'Connor email=x would open a
quoted block at the apostrophe, never close it, and swallow email=x
into the name value (silently dropping the email option). Apostrophes
in real names are common enough that this corrupted realistic input.

Add an at-token-start guard so a quote character only opens a quoted
block when it immediately follows whitespace, =, or the start of the
input. This preserves every canonical case (name="Doe, Jane",
a='1 2', leading quoted tokens) while making mid-value quote
characters literal. The contract is locked down at both the
split_string and parse_kv_string layers.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 8 out of 8 changed files in this pull request and generated 1 comment.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread test/test_create_command.jl Outdated
The previous assertions only checked that ".DS_Store" and ".vscode"
appeared in stdout, which would still pass if `ignore` were silently
flattened into the single string ".DS_Store,.vscode". Assert on the
array formatting (`ignore = [`, `"\\".DS_Store\\""`, `"\\".vscode\\""`)
so this test catches the corruption mode it was written to guard
against.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 8 out of 8 changed files in this pull request and generated 1 comment.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/plugin_option_parser.jl Outdated
The previous docstring said whitespace around keys and values is
stripped, which was misleading: split_string splits on whitespace first,
so 'k= v' and 'k =v' produce separate tokens that don't form a single
option. Spell out that 'key=value' must remain one token and that
whitespace around '=' will not be coalesced.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 8 out of 8 changed files in this pull request and generated no new comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@ultimatile ultimatile changed the title refactor(parser): share plugin-option parser between create and config (#9) refactor(parser): share plugin-option parser between create and config Apr 26, 2026
@ultimatile ultimatile merged commit 98b897e into main Apr 26, 2026
6 checks passed
@ultimatile ultimatile deleted the refactor/issue-9-shared-plugin-option-parser branch April 26, 2026 19:34
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.

Share plugin-option parser between create and config set

2 participants