modcli/output/
markdown.rs1pub fn render_markdown(input: &str) -> String {
8 let mut out = String::with_capacity(input.len() + 32);
9 for line in input.lines() {
10 let trimmed = line.trim_start();
11 let styled_line = if let Some(rest) = trimmed.strip_prefix("### ") {
12 format!("\x1b[1m{}\x1b[0m", render_inline(rest))
13 } else if let Some(rest) = trimmed.strip_prefix("## ") {
14 format!("\x1b[1m{}\x1b[0m", render_inline(rest))
15 } else if let Some(rest) = trimmed.strip_prefix("# ") {
16 format!("\x1b[1m{}\x1b[0m", render_inline(rest))
17 } else if let Some(rest) = trimmed.strip_prefix("- ") {
18 format!(" • {}", render_inline(rest))
19 } else {
20 render_inline(line)
21 };
22 out.push_str(&styled_line);
23 out.push('\n');
24 }
25 out
26}
27
28fn render_inline(s: &str) -> String {
29 let mut out = String::new();
31 let mut i = 0;
32 let bytes = s.as_bytes();
33 while i < bytes.len() {
34 if bytes[i] == b'`' {
35 if let Some(j) = find_next(bytes, i + 1, b'`') {
36 out.push_str("\x1b[7m"); out.push_str(&s[i + 1..j]);
38 out.push_str("\x1b[0m");
39 i = j + 1;
40 continue;
41 }
42 }
43 out.push(bytes[i] as char);
44 i += 1;
45 }
46 let s2 = out;
48 let s3 = replace_enclosed(&s2, "**", "\x1b[1m", "\x1b[0m");
49 replace_enclosed(&s3, "*", "\x1b[3m", "\x1b[0m")
50}
51
52fn find_next(bytes: &[u8], mut i: usize, ch: u8) -> Option<usize> {
53 while i < bytes.len() {
54 if bytes[i] == ch {
55 return Some(i);
56 }
57 i += 1;
58 }
59 None
60}
61
62fn replace_enclosed(s: &str, token: &str, start: &str, end: &str) -> String {
63 let mut out = String::with_capacity(s.len());
64 let mut i = 0usize;
65 let tlen = token.len();
66 let bytes = s.as_bytes();
67 while i < bytes.len() {
68 if i + tlen <= bytes.len() && &s[i..i + tlen] == token {
69 if let Some(j) = find_token(&s[i + tlen..], token) {
70 out.push_str(start);
71 out.push_str(&s[i + tlen..i + tlen + j]);
72 out.push_str(end);
73 i = i + tlen + j + tlen;
74 continue;
75 }
76 }
77 out.push(bytes[i] as char);
78 i += 1;
79 }
80 out
81}
82
83fn find_token(hay: &str, token: &str) -> Option<usize> {
84 hay.find(token)
85}