fur_cli/renderer/
markdown.rs1use std::fs;
2use std::path::Path;
3use serde_json::Value;
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 { return };
74
75 if let Some(att) = msg.attachment {
76 if att.ends_with(".png")
77 || att.ends_with(".jpg")
78 || att.ends_with(".jpeg")
79 || att.ends_with(".gif")
80 {
81 out.push_str(&format!("\n\n\n", att));
82 } else if att.ends_with(".pdf") {
83 out.push_str(&format!(
84 "\n[Attached PDF: {}]({})\n\n",
85 Path::new(&att).file_name().unwrap().to_string_lossy(),
86 att
87 ));
88 } else {
89 out.push_str(&format!("\n[Attachment: {}]\n\n", att));
90 }
91 }
92
93 out.push_str(&format!("**{} [{}]:** {}\n", msg.name, msg.emoji, msg.text));
94 out.push_str(&format!("_{} {} - {}_\n\n", msg.date_str, msg.time_str, label));
95
96 if args.verbose || args.contents {
97 if let Some(path_str) = msg.markdown {
98 if let Ok(contents) = fs::read_to_string(path_str) {
99 let converted = convert_bracket_math_block(&contents);
101 out.push_str(&format!("\n{}\n", converted));
102
103 }
104 }
105 }
106
107 for (bi, block) in msg.branches.iter().enumerate() {
109 let branch_label = format!("{} - Branch {}", label, bi + 1);
110 for cid in block {
111 render_message_to_md(fur_dir, cid, branch_label.clone(), args, avatars, out);
112 }
113 }
114}