146 lines
4.1 KiB
Python
146 lines
4.1 KiB
Python
from dataclasses import dataclass
|
|
from importlib import import_module
|
|
from types import ModuleType
|
|
|
|
import typer
|
|
|
|
from cli.context import EnvOption
|
|
from cli.core import DEFAULT_CORE_SERVICES, CliContextFactory, CliExecutionService
|
|
from cli.exit_codes import DEPENDENCY_ERROR
|
|
from cli.output import CommandResult
|
|
from cli.tui.copy import TUI_COPY
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class TuiDependencyResultFactory:
|
|
"""
|
|
TUI 依赖结果构建器。
|
|
|
|
该对象负责统一构建 Textual/Rich 缺失时的降级结果,
|
|
避免命令注册层继续直接拼接错误负载。
|
|
"""
|
|
|
|
def build_missing_dependency_result(self) -> CommandResult:
|
|
"""
|
|
构建 TUI 可选依赖缺失时的提示结果。
|
|
|
|
:return: 命令结果对象
|
|
"""
|
|
return CommandResult(
|
|
data={
|
|
'ok': False,
|
|
'message': TUI_COPY.build_missing_dependency_message(),
|
|
'hint': TUI_COPY.build_missing_dependency_hint(),
|
|
},
|
|
exit_code=DEPENDENCY_ERROR,
|
|
)
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class TuiAppModuleLoader:
|
|
"""
|
|
TUI 应用模块加载器。
|
|
|
|
该对象负责识别缺失的可选依赖,并在命令执行阶段按需导入
|
|
`cli.tui.app` 模块。
|
|
"""
|
|
|
|
def is_missing_dependency_error(self, exc: ModuleNotFoundError) -> bool:
|
|
"""
|
|
判断模块导入异常是否由 TUI 可选依赖缺失触发。
|
|
|
|
:param exc: 模块导入异常
|
|
:return: 是否为 TUI 依赖缺失
|
|
"""
|
|
missing_module_name = (exc.name or '').split('.', 1)[0]
|
|
return missing_module_name in {'textual', 'rich'}
|
|
|
|
def load(self) -> ModuleType:
|
|
"""
|
|
惰性导入 TUI 应用模块。
|
|
|
|
:return: `cli.tui.app` 模块对象
|
|
"""
|
|
return import_module('cli.tui.app')
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class TuiCommandRegistration:
|
|
"""
|
|
TUI 命令注册器。
|
|
|
|
:param context_factory: CLI 上下文工厂
|
|
:param execution_service: CLI 执行服务
|
|
:param dependency_result_factory: TUI 缺失依赖结果构建器
|
|
:param module_loader: TUI 应用模块加载器
|
|
"""
|
|
|
|
context_factory: CliContextFactory
|
|
execution_service: CliExecutionService
|
|
dependency_result_factory: TuiDependencyResultFactory
|
|
module_loader: TuiAppModuleLoader
|
|
|
|
def handle_tui_command(self, env: str) -> None:
|
|
"""
|
|
执行 `shierfeng tui` 命令。
|
|
|
|
:param env: 当前命令运行环境
|
|
:return: None
|
|
"""
|
|
ctx = self.context_factory.build_readonly(env, 'text')
|
|
try:
|
|
tui_app_module = self.module_loader.load()
|
|
except ModuleNotFoundError as exc:
|
|
if self.module_loader.is_missing_dependency_error(exc):
|
|
self.execution_service.complete_result(
|
|
ctx,
|
|
self.dependency_result_factory.build_missing_dependency_result(),
|
|
)
|
|
return
|
|
raise
|
|
tui_app_module.TUI_APP_RUNNER.run(env)
|
|
|
|
def build(self) -> typer.Typer:
|
|
"""
|
|
构建 `tui` 子应用。
|
|
|
|
:return: `tui` 子应用
|
|
"""
|
|
app = typer.Typer(
|
|
help=TUI_COPY.build_tui_command_help(),
|
|
no_args_is_help=False,
|
|
invoke_without_command=True,
|
|
context_settings={'help_option_names': ['-h', '--help']},
|
|
)
|
|
|
|
@app.callback()
|
|
def tui(
|
|
env: EnvOption = 'dev',
|
|
) -> None:
|
|
"""
|
|
进入只读巡检工作台。
|
|
|
|
:param env: 当前命令运行环境
|
|
:return: None
|
|
"""
|
|
self.handle_tui_command(env)
|
|
|
|
return app
|
|
|
|
def register(self, root_cli: typer.Typer) -> None:
|
|
"""
|
|
向根 Typer 应用注册 `tui` 命令。
|
|
|
|
:param root_cli: 根 Typer 应用
|
|
:return: None
|
|
"""
|
|
root_cli.add_typer(self.build(), name='tui')
|
|
|
|
|
|
TUI_COMMAND_REGISTRATION = TuiCommandRegistration(
|
|
context_factory=DEFAULT_CORE_SERVICES.context_factory,
|
|
execution_service=DEFAULT_CORE_SERVICES.execution_service,
|
|
dependency_result_factory=TuiDependencyResultFactory(),
|
|
module_loader=TuiAppModuleLoader(),
|
|
)
|