138 lines
4.6 KiB
Python
138 lines
4.6 KiB
Python
import asyncio
|
|
from collections.abc import Callable
|
|
from dataclasses import dataclass, field
|
|
from typing import Any
|
|
|
|
from cli.context import CliContext
|
|
from cli.exit_codes import RUNTIME_ERROR
|
|
from cli.output import CommandResult, OutputRenderer
|
|
|
|
|
|
@dataclass
|
|
class CliExecutionService:
|
|
"""
|
|
CLI 执行与结果收口服务。
|
|
|
|
:param output_renderer: 输出渲染器
|
|
"""
|
|
|
|
output_renderer: OutputRenderer = field(default_factory=OutputRenderer)
|
|
|
|
def run_async(self, coroutine: Any) -> Any:
|
|
"""
|
|
执行异步协程并返回结果。
|
|
|
|
:param coroutine: 待执行协程
|
|
:return: 协程执行结果
|
|
"""
|
|
return asyncio.run(coroutine)
|
|
|
|
def complete_payload(
|
|
self,
|
|
ctx: CliContext,
|
|
payload: dict[str, Any],
|
|
*,
|
|
default_exit_code: int = 0,
|
|
) -> None:
|
|
"""
|
|
输出标准字典负载并结束命令。
|
|
|
|
:param ctx: CLI 上下文
|
|
:param payload: 标准结果字典
|
|
:param default_exit_code: 默认退出码
|
|
:return: None
|
|
"""
|
|
self.output_renderer.complete_command(self.build_result(payload, default_exit_code=default_exit_code), ctx)
|
|
|
|
def complete_result(
|
|
self,
|
|
ctx: CliContext,
|
|
result: CommandResult,
|
|
) -> None:
|
|
"""
|
|
输出命令结果对象并结束命令。
|
|
|
|
:param ctx: CLI 上下文
|
|
:param result: 命令执行结果对象
|
|
:return: None
|
|
"""
|
|
self.output_renderer.complete_command(result, ctx)
|
|
|
|
def complete_payload_with_text(
|
|
self,
|
|
ctx: CliContext,
|
|
payload: dict[str, Any],
|
|
*,
|
|
text_builder: Callable[[dict[str, Any]], Any],
|
|
default_exit_code: int = 0,
|
|
text_condition: Callable[[dict[str, Any]], bool] | None = None,
|
|
) -> None:
|
|
"""
|
|
按输出格式收口标准负载,必要时先转换为文本结果。
|
|
|
|
:param ctx: CLI 上下文
|
|
:param payload: 标准结果字典
|
|
:param text_builder: 文本结果构建函数
|
|
:param default_exit_code: 默认退出码
|
|
:param text_condition: 文本模式下是否应用构建函数的判定函数
|
|
:return: None
|
|
"""
|
|
if ctx.output != 'text':
|
|
self.complete_payload(ctx, payload, default_exit_code=default_exit_code)
|
|
return
|
|
|
|
result = self.build_result(payload, default_exit_code=default_exit_code)
|
|
should_build_text = True
|
|
if callable(text_condition) and isinstance(result.data, dict):
|
|
should_build_text = bool(text_condition(result.data))
|
|
if should_build_text and isinstance(result.data, dict):
|
|
result.data = text_builder(result.data)
|
|
self.complete_result(ctx, result)
|
|
|
|
def complete_payload_result(
|
|
self,
|
|
ctx: CliContext,
|
|
payload: dict[str, Any],
|
|
*,
|
|
text_builder: Callable[[dict[str, Any]], Any] | None = None,
|
|
default_exit_code: int = 0,
|
|
text_condition: Callable[[dict[str, Any]], bool] | None = None,
|
|
) -> None:
|
|
"""
|
|
统一收口标准负载,按输出格式决定是否渲染为文本结果。
|
|
|
|
:param ctx: CLI 上下文
|
|
:param payload: 标准结果字典
|
|
:param text_builder: 文本结果构建函数
|
|
:param default_exit_code: 默认退出码
|
|
:param text_condition: 文本模式下是否应用构建函数的判定函数
|
|
:return: None
|
|
"""
|
|
if text_builder is None:
|
|
self.complete_payload(ctx, payload, default_exit_code=default_exit_code)
|
|
return
|
|
self.complete_payload_with_text(
|
|
ctx,
|
|
payload,
|
|
text_builder=text_builder,
|
|
default_exit_code=default_exit_code,
|
|
text_condition=text_condition,
|
|
)
|
|
|
|
@staticmethod
|
|
def build_result(payload: dict[str, Any], *, default_exit_code: int = 0) -> CommandResult:
|
|
"""
|
|
将标准字典负载转换为命令结果对象。
|
|
|
|
:param payload: 标准结果字典
|
|
:param default_exit_code: 默认退出码;当 payload 未显式提供 exit_code 且
|
|
该值仍为 0 时,失败结果会统一回退到 `RUNTIME_ERROR`
|
|
:return: 命令结果对象
|
|
"""
|
|
result_payload = dict(payload)
|
|
exit_code = result_payload.pop('exit_code', None)
|
|
if exit_code is None:
|
|
is_ok = bool(result_payload.get('ok', True))
|
|
exit_code = default_exit_code if is_ok or default_exit_code != 0 else RUNTIME_ERROR
|
|
return CommandResult(data=result_payload, exit_code=exit_code)
|