pub fn extract_img_markers(text: &str) -> (String, Vec<String>) {
extract_markers_with_prefix(text, "<<IMG:")
}
pub fn extract_vid_markers(text: &str) -> (String, Vec<String>) {
extract_markers_with_prefix(text, "<<VID:")
}
pub fn extract_react_marker(text: &str) -> (String, Option<String>) {
const PREFIX: &str = "<<react:";
let mut out = String::with_capacity(text.len());
let mut emoji: Option<String> = None;
let mut in_code = false;
let mut i = 0;
while i < text.len() {
let ch = text[i..].chars().next().expect("i lies on a char boundary");
if ch == '`' {
in_code = !in_code;
out.push(ch);
i += 1;
continue;
}
if !in_code
&& text[i..].starts_with(PREFIX)
&& let Some(rel_end) = text[i..].find(">>")
{
let payload = text[i + PREFIX.len()..i + rel_end].trim();
if is_reaction_emoji(payload) {
if emoji.is_none() {
emoji = Some(payload.to_string());
}
i += rel_end + 2; continue;
}
}
out.push(ch);
i += ch.len_utf8();
}
(out.trim().to_string(), emoji)
}
fn is_reaction_emoji(payload: &str) -> bool {
!payload.is_empty() && payload.chars().count() <= 8 && payload.chars().all(|c| !c.is_ascii())
}
fn extract_markers_with_prefix(text: &str, prefix: &str) -> (String, Vec<String>) {
let mut out = text.to_string();
let mut paths = Vec::new();
let prefix_len = prefix.len();
while let Some(start) = out.find(prefix) {
let Some(rel_end) = out[start..].find(">>") else {
break;
};
let end = start + rel_end + 2; let path = out[start + prefix_len..start + rel_end].trim().to_string();
if !path.is_empty() {
paths.push(path);
}
out.replace_range(start..end, "");
}
(out.trim().to_string(), paths)
}