1use evorule_reactor::Fact;
12
13pub fn fact_to_human(fact: &Fact) -> String {
43 match fact {
44 Fact::Command { id, instruction } => {
45 let instr_type = instruction
46 .get("type")
47 .and_then(|v| v.as_str())
48 .unwrap_or("?");
49 format!("[F{}] Command type={}", id.0, instr_type)
50 }
51 Fact::PayloadUpdate { id, path, .. } => {
52 format!("[F{}] PayloadUpdate path={}", id.0, path)
53 }
54 Fact::StateTransition { id, cause, .. } => {
55 format!("[F{}] StateTransition cause=F{}", id.0, cause.0)
56 }
57 Fact::IoRequest {
58 id, cause, io_type, ..
59 } => {
60 format!(
61 "[F{}] IoRequest cause=F{} io_type={}",
62 id.0,
63 cause.0,
64 io_type.as_str()
65 )
66 }
67 Fact::IoResponse {
68 id,
69 request_id,
70 error,
71 ..
72 } => match error {
73 Some(msg) => format!(
74 "[F{}] IoResponse request_id=F{} error={}",
75 id.0, request_id.0, msg
76 ),
77 None => format!("[F{}] IoResponse request_id=F{} ok", id.0, request_id.0),
78 },
79 Fact::Stable { id, version } => format!("[F{}] Stable version={}", id.0, version),
80 Fact::Error { id, message } => format!("[F{}] Error: {}", id.0, message),
81 }
82}
83
84pub fn facts_to_human(facts: &[Fact]) -> String {
86 facts
87 .iter()
88 .map(fact_to_human)
89 .collect::<Vec<_>>()
90 .join("\n")
91}
92
93pub mod diff_prefix {
95 pub const CHANGED: &str = "[~]";
97 pub const ONLY_A: &str = "[-]";
99 pub const ONLY_B: &str = "[+]";
101}
102
103pub fn format_diff_line(prefix: &str, content: &str) -> String {
105 format!("{} {}", prefix, content)
106}
107
108#[cfg(test)]
109mod tests {
110 #![allow(clippy::unwrap_used)]
111 use super::*;
112 use evorule_reactor::{Fact, FactId, IoType};
113 use evorule_tcb::JsonValue;
114
115 #[test]
116 fn test_fact_to_human_command() {
117 let fact = Fact::Command {
118 id: FactId(1),
119 instruction: JsonValue::object_from_pairs(&[("type", JsonValue::string("noop"))]),
120 };
121 let s = fact_to_human(&fact);
122 assert!(s.contains("[F1] Command type=noop"));
123 }
124
125 #[test]
126 fn test_fact_to_human_error() {
127 let fact = Fact::Error {
128 id: FactId(5),
129 message: "max_steps exceeded".into(),
130 };
131 let s = fact_to_human(&fact);
132 assert!(s.contains("[F5] Error: max_steps exceeded"));
133 }
134
135 #[test]
136 fn test_fact_to_human_stable() {
137 let fact = Fact::Stable {
138 id: FactId(3),
139 version: 1,
140 };
141 let s = fact_to_human(&fact);
142 assert!(s.contains("[F3] Stable version=1"));
143 }
144
145 #[test]
146 fn test_fact_to_human_io_request() {
147 let fact = Fact::IoRequest {
148 id: FactId(4),
149 cause: FactId(2),
150 io_type: IoType::http_get(),
151 params: JsonValue::empty_object(),
152 };
153 let s = fact_to_human(&fact);
154 assert!(s.contains("IoRequest"));
155 assert!(s.contains("io_type=http_get"));
156 }
157
158 #[test]
159 fn test_facts_to_human_multiline() {
160 let facts = vec![
161 Fact::Command {
162 id: FactId(1),
163 instruction: JsonValue::empty_object(),
164 },
165 Fact::Stable {
166 id: FactId(2),
167 version: 1,
168 },
169 ];
170 let s = facts_to_human(&facts);
171 assert_eq!(s.lines().count(), 2);
172 }
173
174 #[test]
175 fn test_format_diff_line() {
176 let s = format_diff_line(diff_prefix::CHANGED, "some content");
177 assert_eq!(s, "[~] some content");
178 }
179}