1const ANSI_CYAN: &str = "\x1b[36m";
12const ANSI_GREEN: &str = "\x1b[32m";
13const ANSI_DIM: &str = "\x1b[2m";
14const ANSI_RESET: &str = "\x1b[0m";
15
16pub fn render_doc(topic: &str, text: &str, colored: bool) -> String {
21 let (c, g, d, n) = if colored {
22 (ANSI_CYAN, ANSI_GREEN, ANSI_DIM, ANSI_RESET)
23 } else {
24 ("", "", "", "")
25 };
26 let rule_len = topic.chars().count().clamp(20, 76);
27 let mut out = String::with_capacity(text.len() + 256);
28 out.push_str(&format!("{c}{topic}{n}\n"));
29 out.push_str(&format!("{d}{}{n}\n", "─".repeat(rule_len)));
30 let mut in_code = false;
31 for line in text.split('\n') {
32 if line.starts_with("```") {
33 in_code = !in_code;
34 continue;
35 }
36 if in_code {
37 out.push_str(&format!("{g} {line}{n}\n"));
38 } else if line.trim().is_empty() {
39 out.push('\n');
40 } else {
41 out.push_str(&render_inline_code(line, c, n));
42 out.push('\n');
43 }
44 }
45 out
46}
47
48fn render_inline_code(line: &str, color: &str, reset: &str) -> String {
53 let mut out = String::with_capacity(line.len() + 64);
54 let mut in_tick = false;
55 for ch in line.chars() {
56 if ch == '`' {
57 if in_tick {
58 out.push_str(reset);
59 } else {
60 out.push_str(color);
61 }
62 in_tick = !in_tick;
63 } else {
64 out.push(ch);
65 }
66 }
67 if in_tick {
68 out.push_str(reset);
69 }
70 out
71}
72
73#[cfg(test)]
74mod tests {
75 use super::*;
76
77 #[test]
78 fn plain_mode_strips_ansi() {
79 let r = render_doc("pmap", "Hello `world`", false);
80 assert!(
81 !r.contains('\x1b'),
82 "plain mode must not emit ANSI: {:?}",
83 r
84 );
85 assert!(r.contains("pmap"));
86 assert!(r.contains("Hello world"), "body text preserved: {:?}", r);
90 }
91
92 #[test]
93 fn colored_mode_wraps_inline_ticks() {
94 let r = render_doc("pmap", "Hello `world`", true);
95 assert!(r.contains("\x1b[36m"), "colored must emit cyan: {:?}", r);
96 assert!(r.contains("\x1b[0m"), "colored must reset: {:?}", r);
97 }
98
99 #[test]
100 fn code_fence_lines_render_green() {
101 let r = render_doc("pmap", "intro\n```perl\nmy $x = 1\n```\nafter", true);
102 assert!(
103 r.contains("\x1b[32m"),
104 "fenced block must emit green: {:?}",
105 r
106 );
107 assert!(!r.contains("```"), "fence markers must be hidden: {:?}", r);
108 }
109}