use super::extract::{document_to_markdown, parse_message_bytes};
const FROM_SEP: &[u8] = b"From ";
pub fn split_mbox(raw: &[u8]) -> Vec<Vec<u8>> {
let mut messages: Vec<Vec<u8>> = Vec::new();
let mut current: Vec<u8> = Vec::new();
let mut started = false;
for line in split_keep_eol(raw) {
if line_starts_with(line, FROM_SEP) {
if started && !current.is_empty() {
messages.push(std::mem::take(&mut current));
}
started = true;
current.clear();
continue; }
if !started {
started = true;
}
if is_escaped_from(line) {
current.extend_from_slice(&line[1..]);
} else {
current.extend_from_slice(line);
}
}
if !current.is_empty() {
messages.push(current);
}
messages
}
fn line_starts_with(line: &[u8], prefix: &[u8]) -> bool {
line.len() >= prefix.len() && &line[..prefix.len()] == prefix
}
fn is_escaped_from(line: &[u8]) -> bool {
let mut i = 0;
while i < line.len() && line[i] == b'>' {
i += 1;
}
i > 0 && line_starts_with(&line[i..], FROM_SEP)
}
fn split_keep_eol(raw: &[u8]) -> Vec<&[u8]> {
let mut out = Vec::new();
let mut start = 0usize;
for (i, &b) in raw.iter().enumerate() {
if b == b'\n' {
out.push(&raw[start..=i]);
start = i + 1;
}
}
if start < raw.len() {
out.push(&raw[start..]);
}
out
}
#[derive(Clone)]
pub struct MboxMessageInfo {
pub index: usize,
pub subject: Option<String>,
pub from: Option<String>,
pub date: Option<String>,
pub message_id: Option<String>,
pub in_reply_to: Vec<String>,
pub references: Vec<String>,
}
pub fn mbox_to_markdown(
raw: &[u8],
) -> (String, Vec<(String, Vec<u8>)>, usize, Vec<MboxMessageInfo>) {
let messages = split_mbox(raw);
let count = messages.len();
let mut out = String::new();
let mut images: Vec<(String, Vec<u8>)> = Vec::new();
out.push_str(&format!(
"# Mailbox — {count} message{}\n\n",
if count == 1 { "" } else { "s" }
));
let mut infos: Vec<MboxMessageInfo> = Vec::with_capacity(count);
for (i, msg_bytes) in messages.iter().enumerate() {
let doc = parse_message_bytes(msg_bytes);
infos.push(MboxMessageInfo {
index: i + 1,
subject: doc.subject.clone(),
from: doc.from.clone(),
date: doc.date.clone(),
message_id: doc.message_id.clone(),
in_reply_to: doc.in_reply_to.clone(),
references: doc.references.clone(),
});
out.push_str(&format!("## Message {}\n\n", i + 1));
out.push_str(&document_to_markdown(&doc, 3));
out.push_str("\n\n");
for (name, bytes) in doc.images {
images.push((format!("msg{}_{name}", i + 1), bytes));
}
}
(out.trim().to_string(), images, count, infos)
}