evorule_cli/commands/run.rs
1// SPDX-License-Identifier: AGPL-3.0-or-later
2// Copyright (C) 2026 EvoRule Project
3// This file is part of EvoRule, licensed under GNU Affero General Public License v3 or later.
4//! `evorule run` —— 加载并执行 JSON 规则,输出 fact log
5//!
6//! # 流程
7//! 1. `io_util::load_rules`:加载规则目录(确定性排序)
8//! 2. `io_util::parse_initial_payload`:解析初始 payload
9//! 3. 构造初始指令 `{"type":"noop"}` 触发 transform 链
10//! 4. `executor::execute`:同步反应器循环(FIFO + max_steps)
11//! 5. `fact_log::write_facts`:输出 fact log(tier1 WAL 格式)
12//!
13//! # fact log 格式
14//! 与 evorule-reactor WAL 文件格式互换,与 evorule-governance auditor 哈希链互通。
15
16use std::path::Path;
17
18use evorule_tcb::JsonValue;
19
20use crate::error::CliError;
21use crate::{executor, fact_log, io_util};
22
23/// 执行 run 子命令
24///
25/// # 参数
26/// - `rules_dir`:规则目录
27/// - `payload`:初始 payload JSON 字符串(可选)
28/// - `payload_file`:初始 payload 文件(可选,优先级低于 payload)
29/// - `output`:输出文件路径(None 则 stdout)
30/// - `max_steps`:最大执行步数上界
31pub fn run(
32 rules_dir: &Path,
33 payload: Option<&str>,
34 payload_file: Option<&Path>,
35 output: Option<&Path>,
36 max_steps: usize,
37) -> Result<(), CliError> {
38 let transforms = io_util::load_rules(rules_dir)?;
39 let initial_payload = io_util::parse_initial_payload(payload, payload_file)?;
40
41 // 初始指令:noop 触发 transform 链
42 // (core_eval 规则通过 branch domain=instruction_type 匹配 noop 后执行 on_true)
43 let initial_instruction = JsonValue::object_from_pairs(&[("type", JsonValue::string("noop"))]);
44
45 tracing::info!(rules = transforms.len(), max_steps, "starting execution");
46
47 let facts = executor::execute(&transforms, initial_payload, initial_instruction, max_steps)?;
48
49 // 输出 fact log(tier1 WAL 格式)
50 fact_log::write_facts(output, &facts)?;
51
52 // stderr 摘要(不影响 fact log 输出)
53 let has_error = facts
54 .iter()
55 .any(|f| matches!(f, evorule_reactor::Fact::Error { .. }));
56 if has_error {
57 tracing::warn!(facts = facts.len(), "execution completed with Error facts");
58 } else {
59 tracing::info!(facts = facts.len(), "execution completed successfully");
60 }
61
62 Ok(())
63}