Coverage for /home/runner/work/bijux-cli/bijux-cli/src/bijux_cli/commands/config/reload.py: 100%
29 statements
« prev ^ index » next coverage.py v7.10.4, created at 2025-08-19 23:36 +0000
« prev ^ index » next coverage.py v7.10.4, created at 2025-08-19 23:36 +0000
1# SPDX-License-Identifier: MIT
2# Copyright © 2025 Bijan Mousavi
4"""Implements the `config reload` subcommand for the Bijux CLI.
6This module contains the logic for manually reloading the application's
7configuration from its source file on disk. It discards any in-memory
8settings and replaces them with the content of the configuration file,
9emitting a structured confirmation upon success.
11Output Contract:
12 * Success: `{"status": "reloaded"}`
13 * Verbose: Adds `{"python": str, "platform": str}` to the payload.
14 * Error: `{"error": str, "code": int}`
16Exit Codes:
17 * `0`: Success.
18 * `2`: The configuration file could not be read or parsed.
19"""
21from __future__ import annotations
23import platform
25import typer
27from bijux_cli.commands.utilities import (
28 ascii_safe,
29 emit_error_and_exit,
30 new_run_command,
31 parse_global_flags,
32)
33from bijux_cli.contracts import ConfigProtocol
34from bijux_cli.core.constants import (
35 HELP_DEBUG,
36 HELP_FORMAT,
37 HELP_NO_PRETTY,
38 HELP_QUIET,
39 HELP_VERBOSE,
40)
41from bijux_cli.core.di import DIContainer
44def reload_config(
45 ctx: typer.Context,
46 quiet: bool = typer.Option(False, "-q", "--quiet", help=HELP_QUIET),
47 verbose: bool = typer.Option(False, "-v", "--verbose", help=HELP_VERBOSE),
48 fmt: str = typer.Option("json", "-f", "--format", help=HELP_FORMAT),
49 pretty: bool = typer.Option(True, "--pretty/--no-pretty", help=HELP_NO_PRETTY),
50 debug: bool = typer.Option(False, "-d", "--debug", help=HELP_DEBUG),
51) -> None:
52 """Reloads the configuration from disk and emits a structured result.
54 This function forces a refresh of the application's configuration from its
55 persistent storage file. It is useful when the configuration has been
56 modified externally. A success or error payload is always emitted.
58 Args:
59 ctx (typer.Context): The Typer context for the CLI.
60 quiet (bool): If True, suppresses all output except for errors.
61 verbose (bool): If True, includes Python/platform details in the output.
62 fmt (str): The output format, "json" or "yaml".
63 pretty (bool): If True, pretty-prints the output.
64 debug (bool): If True, enables debug diagnostics.
66 Returns:
67 None:
69 Raises:
70 SystemExit: Always exits with a contract-compliant status code and
71 payload, indicating success or detailing the error.
72 """
73 flags = parse_global_flags()
75 quiet = flags["quiet"]
76 verbose = flags["verbose"]
77 fmt = flags["format"]
78 pretty = flags["pretty"]
79 debug = flags["debug"]
81 include_runtime = verbose
82 fmt_lower = fmt.lower()
84 command = "config reload"
86 config_svc = DIContainer.current().resolve(ConfigProtocol)
88 try:
89 config_svc.reload()
90 except Exception as exc:
91 emit_error_and_exit(
92 f"Failed to reload config: {exc}",
93 code=2,
94 failure="reload_failed",
95 command=command,
96 fmt=fmt_lower,
97 quiet=quiet,
98 include_runtime=include_runtime,
99 debug=debug,
100 )
102 def payload_builder(include_runtime: bool) -> dict[str, object]:
103 """Builds the payload confirming a successful configuration reload.
105 Args:
106 include_runtime (bool): If True, includes Python and platform info.
108 Returns:
109 dict[str, object]: The structured payload.
110 """
111 payload: dict[str, object] = {"status": "reloaded"}
112 if include_runtime:
113 payload["python"] = ascii_safe(platform.python_version(), "python_version")
114 payload["platform"] = ascii_safe(platform.platform(), "platform")
115 return payload
117 new_run_command(
118 command_name=command,
119 payload_builder=payload_builder,
120 quiet=quiet,
121 verbose=verbose,
122 fmt=fmt_lower,
123 pretty=pretty,
124 debug=debug,
125 )