Skip to main content

nichlink/registry_core/mir/
render.rs

1//! MIR artifact rendering: the human report and the compact JSONL artifact.
2//! MIR artifact 渲染:人类可读报告与紧凑 JSONL artifact。
3
4use std::fmt::Write as _;
5
6use crate::json::json_string;
7
8use super::model::MirGraph;
9
10impl MirGraph {
11    /// Render the human-readable candidate report, with one line per call and
12    /// per local.
13    /// 渲染人类可读的候选报告,每条调用与每个局部变量各一行。
14    pub fn render(&self) -> String {
15        let mut output = String::new();
16        writeln!(output, "MIR CANDIDATES").unwrap();
17        for call in &self.calls {
18            writeln!(
19                output,
20                "  {} -> {} @ MIR line {}",
21                call.caller, call.callee, call.mir_line
22            )
23            .unwrap();
24        }
25        if self.calls.is_empty() {
26            output.push_str("  (no static call candidates)\n");
27        }
28        writeln!(output, "LOCALS").unwrap();
29        for local in &self.locals {
30            writeln!(
31                output,
32                "  {}::{}: {} @ MIR line {}",
33                local.function, local.name, local.type_name, local.mir_line
34            )
35            .unwrap();
36        }
37        output
38    }
39
40    /// Emit a compact, line-oriented artifact that build tooling can consume.
41    /// 输出构建工具可消费的紧凑逐行 artifact。
42    ///
43    /// Every string goes through the workspace's one JSON encoder. A local
44    /// `replace`-based copy used to sit here, and it emitted a raw tab or newline
45    /// for a name that contained one: the record stayed readable to this crate's
46    /// lenient parser and became invalid for every strict one. The pinning test
47    /// is `a_control_character_in_a_name_stays_escaped`.
48    /// 每个字符串都经过工作区唯一的 JSON 编码器。这里以前有一份基于 `replace` 的本地副本,
49    /// 遇到含制表符或换行的名字时会原样写出:记录对本 crate 的宽松解析器仍然可读,对任何严格
50    /// 解析器都非法。钉住它的是 `a_control_character_in_a_name_stays_escaped`。
51    pub fn to_jsonl(&self) -> String {
52        let mut output = String::new();
53        for function in &self.functions {
54            writeln!(
55                output,
56                "{{\"kind\":\"function\",\"name\":{}}}",
57                json_string(function)
58            )
59            .unwrap();
60        }
61        for call in &self.calls {
62            writeln!(
63                output,
64                "{{\"kind\":\"call\",\"caller\":{},\"callee\":{},\"mir_line\":{}}}",
65                json_string(&call.caller),
66                json_string(&call.callee),
67                call.mir_line
68            )
69            .unwrap();
70        }
71        for local in &self.locals {
72            writeln!(
73                output,
74                "{{\"kind\":\"local\",\"function\":{},\"name\":{},\"type\":{},\"mir_line\":{}}}",
75                json_string(&local.function),
76                json_string(&local.name),
77                json_string(&local.type_name),
78                local.mir_line
79            )
80            .unwrap();
81        }
82        output
83    }
84}
85
86#[cfg(test)]
87mod tests {
88    use super::MirGraph;
89
90    #[test]
91    fn jsonl_artifact_round_trips() {
92        let graph = MirGraph::from_jsonl(
93            "{\"kind\":\"function\",\"name\":\"crate::a\"}\n{\"kind\":\"call\",\"caller\":\"crate::a\",\"callee\":\"crate::b\",\"mir_line\":3}\n",
94        )
95        .unwrap();
96        let parsed = MirGraph::from_jsonl(&graph.to_jsonl()).unwrap();
97        assert_eq!(parsed, graph);
98    }
99
100    /// The artifact must stay valid for a strict reader, which is the whole point
101    /// of JSONL. This crate's own parser is lenient, so a round trip cannot show
102    /// the difference; asserting the absence of raw control characters can.
103    /// 工件必须对严格读取器保持合法,这正是 JSONL 的意义。本 crate 自己的解析器很宽松,
104    /// 因此往返看不出差别;断言"不存在原样控制字符"可以。
105    #[test]
106    fn a_control_character_in_a_name_stays_escaped() {
107        let graph = MirGraph::from_jsonl(
108            "{\"kind\":\"function\",\"name\":\"a\\tb\"}\n{\"kind\":\"call\",\"caller\":\"a\\tb\",\"callee\":\"c\",\"mir_line\":1}\n",
109        )
110        .unwrap();
111        let jsonl = graph.to_jsonl();
112        // The newline *between* records is the format; a control character
113        // *inside* a record is the defect, so the check is per line.
114        // 记录之间的换行是格式本身;记录**内部**的控制字符才是缺陷,因此逐行检查。
115        for line in jsonl.lines() {
116            assert!(
117                !line.chars().any(|character| character < '\u{20}'),
118                "a raw control character makes the line invalid for a strict reader: {line:?}"
119            );
120        }
121        assert!(jsonl.contains(r#""name":"a\tb""#), "{jsonl:?}");
122    }
123}