Coverage for /home/runner/work/bijux-cli/bijux-cli/src/bijux_cli/commands/config/unset.py: 100%
31 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 unset` subcommand for the Bijux CLI.
6This module contains the logic for removing a key-value pair from the active
7configuration store. It provides a structured, machine-readable response to
8confirm the deletion or report an error, such as if the key does not exist.
10Output Contract:
11 * Success: `{"status": "deleted", "key": str}`
12 * Verbose: Adds `{"python": str, "platform": str}` to the payload.
13 * Error: `{"error": str, "code": int}`
15Exit Codes:
16 * `0`: Success.
17 * `1`: An unexpected error occurred while accessing the configuration.
18 * `2`: The specified key was not found in the configuration.
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 unset_config(
45 ctx: typer.Context,
46 key: str = typer.Argument(..., help="Key to remove"),
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 """Removes a key from the active configuration store.
55 This function orchestrates the `unset` operation. It manually parses global
56 flags, resolves the configuration service, attempts to remove the specified
57 key, and then uses the `new_run_command` helper to emit a structured
58 payload confirming the action.
60 Args:
61 ctx (typer.Context): The Typer context for the CLI.
62 key (str): The configuration key to remove.
63 quiet (bool): If True, suppresses all output except for errors.
64 verbose (bool): If True, includes Python/platform details in the output.
65 fmt (str): The output format, "json" or "yaml".
66 pretty (bool): If True, pretty-prints the output.
67 debug (bool): If True, enables debug diagnostics.
69 Returns:
70 None:
72 Raises:
73 SystemExit: Always exits with a contract-compliant status code and
74 payload, indicating success or detailing the error.
75 """
76 flags = parse_global_flags()
78 quiet = flags["quiet"]
79 verbose = flags["verbose"]
80 fmt = flags["format"]
81 pretty = flags["pretty"]
82 debug = flags["debug"]
84 include_runtime = verbose
85 fmt_lower = fmt.lower()
87 command = "config unset"
89 config_svc = DIContainer.current().resolve(ConfigProtocol)
91 try:
92 config_svc.unset(key)
93 except KeyError:
94 emit_error_and_exit(
95 f"Config key not found: {key}",
96 code=2,
97 failure="not_found",
98 command=command,
99 fmt=fmt_lower,
100 quiet=quiet,
101 include_runtime=include_runtime,
102 debug=debug,
103 extra={"key": key},
104 )
105 except Exception as exc:
106 emit_error_and_exit(
107 f"Failed to unset config: {exc}",
108 code=1,
109 failure="unset_failed",
110 command=command,
111 fmt=fmt_lower,
112 quiet=quiet,
113 include_runtime=include_runtime,
114 debug=debug,
115 )
117 def payload_builder(include_runtime: bool) -> dict[str, object]:
118 """Builds the payload confirming a key was deleted.
120 Args:
121 include_runtime (bool): If True, includes Python and platform info.
123 Returns:
124 dict[str, object]: The structured payload.
125 """
126 payload: dict[str, object] = {"status": "deleted", "key": key}
127 if include_runtime:
128 payload["python"] = ascii_safe(platform.python_version(), "python_version")
129 payload["platform"] = ascii_safe(platform.platform(), "platform")
130 return payload
132 new_run_command(
133 command_name=command,
134 payload_builder=payload_builder,
135 quiet=quiet,
136 verbose=verbose,
137 fmt=fmt_lower,
138 pretty=pretty,
139 debug=debug,
140 )