fur_cli/renderer/
markdown.rs1use serde_json::Value;
2use std::fs;
3use std::path::Path;
4
5use crate::commands::timeline::TimelineArgs;
6use crate::renderer::utils::load_message;
7
8fn escape_percent_in_math(text: &str) -> String {
10 let mut out = String::new();
11 let mut chars = text.chars().peekable();
12 let mut in_math = false;
13
14 while let Some(c) = chars.next() {
15 if c == '$' {
16 in_math = !in_math;
18 out.push('$');
19 continue;
20 }
21
22 if in_math && c == '%' {
23 out.push_str("\\%");
24 continue;
25 }
26
27 out.push(c);
28 }
29
30 out
31}
32
33fn convert_bracket_math_block(text: &str) -> String {
35 let mut out = String::new();
36 let mut lines = text.lines().peekable();
37
38 while let Some(line) = lines.next() {
39 if line.trim() == "[" {
40 let mut block = String::new();
41
42 while let Some(inner) = lines.next() {
43 if inner.trim() == "]" {
44 break;
45 }
46 block.push_str(inner);
47 block.push('\n');
48 }
49
50 let math = format!("$\n{}$", escape_percent_in_math(&block));
52 out.push_str(&math);
53 out.push('\n');
54 continue;
55 }
56
57 out.push_str(line);
58 out.push('\n');
59 }
60
61 escape_percent_in_math(&out)
63}
64
65pub fn render_message_to_md(
66 fur_dir: &Path,
67 msg_id: &str,
68 label: String,
69 args: &TimelineArgs,
70 avatars: &Value,
71 out: &mut String,
72) {
73 let Some(msg) = load_message(fur_dir, msg_id, avatars) else {
74 return;
75 };
76
77 if let Some(att) = msg.attachment {
78 if att.ends_with(".png")
79 || att.ends_with(".jpg")
80 || att.ends_with(".jpeg")
81 || att.ends_with(".gif")
82 {
83 out.push_str(&format!("\n\n\n", att));
84 } else if att.ends_with(".pdf") {
85 out.push_str(&format!(
86 "\n[Attached PDF: {}]({})\n\n",
87 Path::new(&att).file_name().unwrap().to_string_lossy(),
88 att
89 ));
90 } else {
91 out.push_str(&format!("\n[Attachment: {}]\n\n", att));
92 }
93 }
94
95 out.push_str(&format!("**{} [{}]:** {}\n", msg.name, msg.emoji, msg.text));
96 out.push_str(&format!(
97 "_{} {} - {}_\n\n",
98 msg.date_str, msg.time_str, label
99 ));
100
101 if args.verbose || args.contents {
102 if let Some(path_str) = msg.markdown {
103 if let Ok(contents) = fs::read_to_string(path_str) {
104 let converted = convert_bracket_math_block(&contents);
106 out.push_str(&format!("\n{}\n", converted));
107 }
108 }
109 }
110
111 for (bi, block) in msg.branches.iter().enumerate() {
113 let branch_label = format!("{} - Branch {}", label, bi + 1);
114 for cid in block {
115 render_message_to_md(fur_dir, cid, branch_label.clone(), args, avatars, out);
116 }
117 }
118}