use std::path::{Path, PathBuf};
use chrono::{DateTime, Datelike, Utc};
const UNKNOWN_DATE_BUCKET: &str = "unknown-date";
pub(crate) fn shard_path(archive_root: &Path, id: &str, date: Option<DateTime<Utc>>) -> PathBuf {
let mut path = archive_root.join("messages");
match date {
Some(date) => {
path.push(format!("{:04}", date.year()));
path.push(format!("{:02}", date.month()));
path.push(format!("{:02}", date.day()));
}
None => path.push(UNKNOWN_DATE_BUCKET),
}
path.push(format!("{id}.eml"));
path
}
pub(crate) fn attachments_dir(
archive_root: &Path,
id: &str,
date: Option<DateTime<Utc>>,
) -> PathBuf {
let mut path = shard_path(archive_root, id, date);
path.set_extension("");
path.push("attachments");
path
}
#[cfg(test)]
#[allow(clippy::unwrap_used, clippy::expect_used)]
mod tests {
use super::*;
use chrono::TimeZone;
fn date(y: i32, m: u32, d: u32) -> DateTime<Utc> {
Utc.with_ymd_and_hms(y, m, d, 0, 0, 0).unwrap()
}
#[test]
fn shard_path_places_eml_under_year_month_day() {
let path = shard_path(Path::new("/archive"), "abc123", Some(date(2026, 3, 5)));
assert_eq!(
path,
PathBuf::from("/archive/messages/2026/03/05/abc123.eml")
);
}
#[test]
fn shard_path_zero_pads_month_and_day() {
let path = shard_path(Path::new("/archive"), "id1", Some(date(2026, 1, 2)));
assert_eq!(path, PathBuf::from("/archive/messages/2026/01/02/id1.eml"));
}
#[test]
fn shard_path_falls_back_to_unknown_date_bucket() {
let path = shard_path(Path::new("/archive"), "id1", None);
assert_eq!(
path,
PathBuf::from("/archive/messages/unknown-date/id1.eml")
);
}
#[test]
fn shard_path_is_deterministic() {
let root = Path::new("/archive");
let d = Some(date(2026, 6, 1));
assert_eq!(shard_path(root, "x", d), shard_path(root, "x", d));
}
#[test]
fn shard_path_groups_same_day_messages_into_one_directory() {
let root = Path::new("/archive");
let d = Some(date(2026, 6, 1));
let a = shard_path(root, "id-a", d);
let b = shard_path(root, "id-b", d);
assert_eq!(a.parent(), b.parent());
}
#[test]
fn shard_path_separates_different_days() {
let root = Path::new("/archive");
let a = shard_path(root, "id", Some(date(2026, 6, 1)));
let b = shard_path(root, "id", Some(date(2026, 6, 2)));
assert_ne!(a.parent(), b.parent());
}
#[test]
fn attachments_dir_places_dir_under_year_month_day() {
let dir = attachments_dir(Path::new("/archive"), "abc123", Some(date(2026, 3, 5)));
assert_eq!(
dir,
PathBuf::from("/archive/messages/2026/03/05/abc123/attachments")
);
}
#[test]
fn attachments_dir_falls_back_to_unknown_date_bucket() {
let dir = attachments_dir(Path::new("/archive"), "id1", None);
assert_eq!(
dir,
PathBuf::from("/archive/messages/unknown-date/id1/attachments")
);
}
#[test]
fn attachments_dir_is_sibling_of_the_eml_file() {
let root = Path::new("/archive");
let d = Some(date(2026, 6, 1));
let eml = shard_path(root, "id", d);
let dir = attachments_dir(root, "id", d);
assert_eq!(dir.parent().unwrap().parent(), eml.parent());
}
}