Skip to content

Commit 3aa99ae

Browse files
committed
feat(cli/fmt[style]) Add --style flag for bidirectional restyling
why: fmt could only normalize toward the standard dict-with-repo-key form. With the central config-style formatter now in place, fmt can convert entries to concise (bare URL string) or verbose (full dict with remotes) as well, letting users pick a house style for their config. what: - Add --style {concise,standard,verbose} to create_fmt_subparser - Thread the style through format_config_file -> format_single_config -> format_config; the latter calls apply_config_style after sorting and surfaces lossy-conversion warnings - Resolve the flag via settings.resolve_style at the CLI boundary, restyling only when --style is passed (keeps save_config, not save_config_yaml) - Cover the conversion with a parametrized format_config test
1 parent 5e6882f commit 3aa99ae

3 files changed

Lines changed: 81 additions & 2 deletions

File tree

src/vcspull/cli/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
from vcspull.__about__ import __version__
1414
from vcspull.log import setup_logger
1515

16+
from .._internal.settings import resolve_style
1617
from ._formatter import VcspullHelpFormatter
1718
from .add import add_repo, create_add_subparser, handle_add_command
1819
from .discover import create_discover_subparser, discover_repos
@@ -587,6 +588,7 @@ def cli(_args: list[str] | None = None) -> None:
587588
args.write,
588589
args.all,
589590
merge_roots=args.merge_roots,
591+
style=resolve_style(args.style) if args.style else None,
590592
)
591593
elif args.subparser_name == "migrate":
592594
migrate_config_file(

src/vcspull/cli/fmt.py

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
from colorama import Fore, Style
1414

1515
from vcspull._internal.config_reader import DuplicateAwareConfigReader
16+
from vcspull._internal.config_style import apply_config_style
1617
from vcspull._internal.private_path import PrivatePath
1718
from vcspull.config import (
1819
find_config_files,
@@ -23,6 +24,7 @@
2324
normalize_workspace_roots,
2425
save_config,
2526
)
27+
from vcspull.types import ConfigStyle
2628

2729
log = logging.getLogger(__name__)
2830

@@ -61,6 +63,13 @@ def create_fmt_subparser(parser: argparse.ArgumentParser) -> None:
6163
action="store_false",
6264
help="Do not merge duplicate workspace roots when formatting",
6365
)
66+
parser.add_argument(
67+
"--style",
68+
dest="style",
69+
choices=["concise", "standard", "verbose"],
70+
default=None,
71+
help="Convert repo entries to the given style (concise, standard, verbose)",
72+
)
6473
parser.set_defaults(merge_roots=True)
6574

6675

@@ -181,13 +190,18 @@ def _classify_fmt_action(repo_data: t.Any) -> tuple[FmtAction, t.Any]:
181190
return FmtAction.NO_CHANGE, repo_data
182191

183192

184-
def format_config(config_data: dict[str, t.Any]) -> tuple[dict[str, t.Any], int]:
193+
def format_config(
194+
config_data: dict[str, t.Any],
195+
style: ConfigStyle | None = None,
196+
) -> tuple[dict[str, t.Any], int]:
185197
"""Format vcspull configuration for consistency.
186198
187199
Parameters
188200
----------
189201
config_data : dict
190202
Raw configuration data
203+
style : ConfigStyle | None
204+
When set, convert all repo entries to this style after sorting.
191205
192206
Returns
193207
-------
@@ -224,6 +238,20 @@ def format_config(config_data: dict[str, t.Any]) -> tuple[dict[str, t.Any], int]
224238
if list(config_data.keys()) != sorted(config_data.keys()):
225239
changes += 1
226240

241+
if style is not None:
242+
formatted, style_changes, style_warnings = apply_config_style(
243+
formatted,
244+
style=style,
245+
)
246+
changes += style_changes
247+
for warning in style_warnings:
248+
log.warning(
249+
"%s•%s %s",
250+
Fore.YELLOW,
251+
Style.RESET_ALL,
252+
warning,
253+
)
254+
227255
return formatted, changes
228256

229257

@@ -232,6 +260,7 @@ def format_single_config(
232260
write: bool,
233261
*,
234262
merge_roots: bool,
263+
style: ConfigStyle | None = None,
235264
) -> bool:
236265
"""Format a single vcspull configuration file.
237266
@@ -342,7 +371,7 @@ def format_single_config(
342371
for message in duplicate_merge_conflicts:
343372
log.warning(message)
344373

345-
formatted_config, change_count = format_config(normalized_config)
374+
formatted_config, change_count = format_config(normalized_config, style=style)
346375
change_count += normalization_changes + duplicate_merge_changes
347376

348377
if change_count == 0:
@@ -484,6 +513,7 @@ def format_config_file(
484513
format_all: bool = False,
485514
*,
486515
merge_roots: bool = True,
516+
style: ConfigStyle | None = None,
487517
) -> None:
488518
"""Format vcspull configuration file(s).
489519
@@ -549,6 +579,7 @@ def format_config_file(
549579
config_file,
550580
write,
551581
merge_roots=merge_roots,
582+
style=style,
552583
):
553584
success_count += 1
554585

@@ -602,4 +633,5 @@ def format_config_file(
602633
config_file_path,
603634
write,
604635
merge_roots=merge_roots,
636+
style=style,
605637
)

tests/cli/test_fmt.py

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
normalize_workspace_roots,
1919
workspace_root_label,
2020
)
21+
from vcspull.types import ConfigStyle
2122

2223
if t.TYPE_CHECKING:
2324
from _pytest.logging import LogCaptureFixture
@@ -627,3 +628,47 @@ def test_classify_fmt_action(
627628
"""Test _classify_fmt_action covers all permutations."""
628629
action, _result = _classify_fmt_action(repo_data)
629630
assert action == expected_action
631+
632+
633+
class FmtStyleFixture(t.NamedTuple):
634+
"""Fixture for format_config style conversion."""
635+
636+
test_id: str
637+
style: ConfigStyle
638+
expected_entry: object
639+
640+
641+
FMT_STYLE_FIXTURES: list[FmtStyleFixture] = [
642+
FmtStyleFixture(
643+
test_id="concise-collapses-to-string",
644+
style=ConfigStyle.CONCISE,
645+
expected_entry="git+https://github.com/pallets/flask.git",
646+
),
647+
FmtStyleFixture(
648+
test_id="standard-uses-repo-dict",
649+
style=ConfigStyle.STANDARD,
650+
expected_entry={"repo": "git+https://github.com/pallets/flask.git"},
651+
),
652+
]
653+
654+
655+
@pytest.mark.parametrize(
656+
list(FmtStyleFixture._fields),
657+
FMT_STYLE_FIXTURES,
658+
ids=[fixture.test_id for fixture in FMT_STYLE_FIXTURES],
659+
)
660+
def test_format_config_applies_style(
661+
test_id: str,
662+
style: ConfigStyle,
663+
expected_entry: object,
664+
) -> None:
665+
"""format_config(style=...) restyles entries to the requested form."""
666+
config_data = {
667+
"~/code/": {"flask": {"repo": "git+https://github.com/pallets/flask.git"}},
668+
}
669+
670+
formatted, changes = format_config(config_data, style=style)
671+
672+
assert formatted["~/code/"]["flask"] == expected_entry
673+
if style is ConfigStyle.CONCISE:
674+
assert changes >= 1

0 commit comments

Comments
 (0)