Coverage for src/prosemark/cli/structure.py: 100%
38 statements
« prev ^ index » next coverage.py v7.8.0, created at 2025-09-24 18:08 +0000
« prev ^ index » next coverage.py v7.8.0, created at 2025-09-24 18:08 +0000
1"""CLI command for displaying project structure."""
3import json
4from pathlib import Path
5from typing import TYPE_CHECKING, Any, Union
7import click
9from prosemark.adapters.binder_repo_fs import BinderRepoFs
10from prosemark.adapters.logger_stdout import LoggerStdout
11from prosemark.app.use_cases import ShowStructure
12from prosemark.domain.binder import Item
13from prosemark.exceptions import FileSystemError
15if TYPE_CHECKING: # pragma: no cover
16 from prosemark.domain.models import BinderItem
19@click.command()
20@click.option(
21 '--format',
22 '-f',
23 'output_format',
24 default='tree',
25 type=click.Choice(['tree', 'json']),
26 help='Output format',
27)
28@click.option('--path', '-p', type=click.Path(path_type=Path), help='Project directory')
29def structure_command(output_format: str, path: Path | None) -> None:
30 """Display project hierarchy."""
31 try:
32 project_root = path or Path.cwd()
34 # Wire up dependencies
35 binder_repo = BinderRepoFs(project_root)
36 logger = LoggerStdout()
38 # Execute use case
39 interactor = ShowStructure(
40 binder_repo=binder_repo,
41 logger=logger,
42 )
44 structure_str = interactor.execute()
46 if output_format == 'tree':
47 click.echo('Project Structure:')
48 click.echo(structure_str)
49 elif output_format == 'json': # pragma: no branch
50 # For JSON format, we need to convert the tree to JSON
51 binder = binder_repo.load()
53 def item_to_dict(item: Union[Item, 'BinderItem']) -> dict[str, Any]:
54 result: dict[str, Any] = {
55 'display_title': item.display_title,
56 }
57 node_id = item.id if hasattr(item, 'id') else (item.node_id if hasattr(item, 'node_id') else None)
58 if node_id:
59 result['node_id'] = str(node_id)
60 item_children = item.children if hasattr(item, 'children') else []
61 if item_children:
62 result['children'] = [item_to_dict(child) for child in item_children]
63 return result
65 data: dict[str, list[dict[str, Any]]] = {'roots': [item_to_dict(item) for item in binder.roots]}
66 click.echo(json.dumps(data, indent=2))
68 except FileSystemError as e:
69 click.echo(f'Error: {e}', err=True)
70 raise SystemExit(1) from e