Coverage for /home/runner/work/bijux-cli/bijux-cli/src/bijux_cli/commands/config/load.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 load` subcommand for the Bijux CLI.
6This module contains the logic for replacing the application's entire
7configuration with the contents of a specified file. It discards any
8in-memory settings and loads the new configuration, emitting a structured
9confirmation upon success.
11Output Contract:
12 * Success: `{"status": "loaded", "file": str}`
13 * Verbose: Adds `{"python": str, "platform": str}` to the payload.
14 * Error: `{"error": str, "code": int}`
16Exit Codes:
17 * `0`: Success.
18 * `2`: The specified file could not be found, 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 load_config(
45 ctx: typer.Context,
46 path: str = typer.Argument(..., help="Path to load from"),
47 quiet: bool = typer.Option(False, "-q", "--quiet", help=HELP_QUIET),
48 verbose: bool = typer.Option(False, "-v", "--verbose", help=HELP_VERBOSE),
49 fmt: str = typer.Option("json", "-f", "--format", help=HELP_FORMAT),
50 pretty: bool = typer.Option(True, "--pretty/--no-pretty", help=HELP_NO_PRETTY),
51 debug: bool = typer.Option(False, "-d", "--debug", help=HELP_DEBUG),
52) -> None:
53 """Loads configuration from a specified file.
55 This function replaces the current in-memory configuration with the
56 contents of the file at the given path. It provides a structured payload
57 to confirm the operation was successful.
59 Args:
60 ctx (typer.Context): The Typer context for the CLI.
61 path (str): The path to the configuration file to load.
62 quiet (bool): If True, suppresses all output except for errors.
63 verbose (bool): If True, includes Python/platform details in the output.
64 fmt (str): The output format, "json" or "yaml".
65 pretty (bool): If True, pretty-prints the output.
66 debug (bool): If True, enables debug diagnostics.
68 Returns:
69 None:
71 Raises:
72 SystemExit: Always exits with a contract-compliant status code and
73 payload, indicating success or detailing the error.
74 """
75 flags = parse_global_flags()
77 quiet = flags["quiet"]
78 verbose = flags["verbose"]
79 fmt = flags["format"]
80 pretty = flags["pretty"]
81 debug = flags["debug"]
83 include_runtime = verbose
84 fmt_lower = fmt.lower()
86 command = "config load"
88 config_svc = DIContainer.current().resolve(ConfigProtocol)
90 try:
91 config_svc.load(path)
92 except Exception as exc:
93 emit_error_and_exit(
94 f"Failed to load config: {exc}",
95 code=2,
96 failure="load_failed",
97 command=command,
98 fmt=fmt_lower,
99 quiet=quiet,
100 include_runtime=include_runtime,
101 debug=debug,
102 extra={"path": path},
103 )
105 def payload_builder(include_runtime: bool) -> dict[str, object]:
106 """Builds the payload confirming a successful configuration load.
108 Args:
109 include_runtime (bool): If True, includes Python and platform info.
111 Returns:
112 dict[str, object]: The structured payload.
113 """
114 payload: dict[str, object] = {"status": "loaded", "file": path}
115 if include_runtime:
116 payload["python"] = ascii_safe(platform.python_version(), "python_version")
117 payload["platform"] = ascii_safe(platform.platform(), "platform")
118 return payload
120 new_run_command(
121 command_name=command,
122 payload_builder=payload_builder,
123 quiet=quiet,
124 verbose=verbose,
125 fmt=fmt_lower,
126 pretty=pretty,
127 debug=debug,
128 )