Skip to content

Commit 412b49c

Browse files
committed
✨ add Typer CLI structure and command registration
1 parent efd2ab3 commit 412b49c

12 files changed

Lines changed: 180 additions & 1 deletion

File tree

pyproject.toml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,16 @@ dependencies = [
1616
"pydantic>=2.12.5,<3",
1717
"pyscard>=2.3.1,<3",
1818
"python-json-logger>=4.0.0,<5",
19+
"typer>=0.20.0,<1",
1920
]
2021

22+
[project.scripts]
23+
schnee = "schnee.controllers.cli.main:main"
24+
25+
[build-system]
26+
requires = ["hatchling"]
27+
build-backend = "hatchling.build"
28+
2129
[dependency-groups]
2230
dev = [
2331
"nox>=2025.11.12",

src/schnee/controllers/__init__.py

Whitespace-only changes.

src/schnee/controllers/cli/__init__.py

Whitespace-only changes.
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
from __future__ import annotations
2+
3+
from typing import TYPE_CHECKING
4+
5+
if TYPE_CHECKING:
6+
import typer
7+
8+
9+
def register_commands(_app: typer.Typer) -> None:
10+
"""Register CLI commands."""
11+
12+
@_app.callback(invoke_without_command=True)
13+
def root() -> None:
14+
"""NFC/RFID tag authentication and encryption tools."""
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
from __future__ import annotations
2+
3+
from typing import TYPE_CHECKING
4+
5+
import typer
6+
7+
if TYPE_CHECKING:
8+
from schnee.services.base import ServiceError
9+
10+
SERVICE_ERROR_EXIT_CODE = 1
11+
12+
13+
def exit_for_service_error(exc: ServiceError) -> typer.Exit:
14+
"""Render a service-level error and return a CLI exit exception."""
15+
typer.echo(exc.msg, err=True)
16+
return typer.Exit(code=SERVICE_ERROR_EXIT_CODE)

src/schnee/controllers/cli/main.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
from __future__ import annotations
2+
3+
import typer
4+
5+
from schnee.controllers.cli.commands import register_commands
6+
7+
8+
def create_app() -> typer.Typer:
9+
"""Create the root Typer application."""
10+
app = typer.Typer(
11+
help="NFC/RFID tag authentication and encryption tools.",
12+
no_args_is_help=True,
13+
)
14+
register_commands(app)
15+
return app
16+
17+
18+
app = create_app()
19+
20+
21+
def main() -> None:
22+
"""Run the schnee CLI."""
23+
app()
24+
25+
26+
if __name__ == "__main__":
27+
main()
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
from __future__ import annotations
2+
3+
import json
4+
5+
import typer
6+
7+
8+
def echo_text(value: str) -> None:
9+
"""Write plain text output."""
10+
typer.echo(value)
11+
12+
13+
def echo_json(value: object) -> None:
14+
"""Write JSON output."""
15+
typer.echo(json.dumps(value, indent=2, sort_keys=True))
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
from __future__ import annotations
2+
3+
4+
def normalize_backend_name(value: str) -> str:
5+
"""Normalize a backend name from CLI input."""
6+
return value.strip()

tests/schnee/controllers/__init__.py

Whitespace-only changes.

tests/schnee/controllers/cli/__init__.py

Whitespace-only changes.

0 commit comments

Comments
 (0)