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

1# SPDX-License-Identifier: MIT 

2# Copyright © 2025 Bijan Mousavi 

3 

4"""Implements the `config load` subcommand for the Bijux CLI. 

5 

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. 

10 

11Output Contract: 

12 * Success: `{"status": "loaded", "file": str}` 

13 * Verbose: Adds `{"python": str, "platform": str}` to the payload. 

14 * Error: `{"error": str, "code": int}` 

15 

16Exit Codes: 

17 * `0`: Success. 

18 * `2`: The specified file could not be found, read, or parsed. 

19""" 

20 

21from __future__ import annotations 

22 

23import platform 

24 

25import typer 

26 

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 

42 

43 

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. 

54 

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. 

58 

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. 

67 

68 Returns: 

69 None: 

70 

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() 

76 

77 quiet = flags["quiet"] 

78 verbose = flags["verbose"] 

79 fmt = flags["format"] 

80 pretty = flags["pretty"] 

81 debug = flags["debug"] 

82 

83 include_runtime = verbose 

84 fmt_lower = fmt.lower() 

85 

86 command = "config load" 

87 

88 config_svc = DIContainer.current().resolve(ConfigProtocol) 

89 

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 ) 

104 

105 def payload_builder(include_runtime: bool) -> dict[str, object]: 

106 """Builds the payload confirming a successful configuration load. 

107 

108 Args: 

109 include_runtime (bool): If True, includes Python and platform info. 

110 

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 

119 

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 )