use std::fmt;
use crate::fields::Fields;
#[derive(Clone, Debug, Default, Eq, PartialEq)]
pub struct Records<'a> {
pub items: Vec<Record<'a>>,
}
impl<'a> Records<'a> {
pub fn push(&mut self, record: Record<'a>) {
self.items.push(record);
}
}
impl fmt::Display for Records<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
for (i, record) in self.items.iter().enumerate() {
if i > 0 {
writeln!(f)?;
}
write!(f, "{record}")?;
}
Ok(())
}
}
#[derive(Clone, Debug, Default, Eq, PartialEq)]
pub struct Record<'a> {
pub title: String,
pub state: Vec<String>,
pub fields: Fields<'a>,
pub body: String,
}
impl fmt::Display for Record<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(&self.title)?;
if !self.state.is_empty() {
write!(f, " ({})", self.state.join(", "))?;
}
writeln!(f)?;
write!(f, "{}", self.fields)?;
if self.body.is_empty() {
return Ok(());
}
writeln!(f)?;
for line in self.body.lines() {
match line {
"" => writeln!(f)?,
line => writeln!(f, " {line}")?,
}
}
Ok(())
}
}
#[cfg(test)]
mod tests {
use crate::{fields::Fields, records::*};
fn envelope(id: &str, state: &[&str], from: &str, subject: &str) -> Record<'static> {
let mut fields = Fields::default();
fields.push("From", from);
fields.push("Date", "2026-11-11 10:00+01:00");
Record {
title: String::from(id),
state: state.iter().map(|word| word.to_string()).collect(),
fields,
body: String::from(subject),
}
}
#[test]
fn items_print_like_git_log() {
let mut records = Records::default();
let state = ["unread", "flagged"];
records.push(envelope("42", &state, "Alice <a@example.org>", "Hi"));
records.push(envelope("41", &[], "Carol <c@example.org>", "Re: Hi"));
let expected = "\
42 (unread, flagged)
From: Alice <a@example.org>
Date: 2026-11-11 10:00+01:00
Hi
41
From: Carol <c@example.org>
Date: 2026-11-11 10:00+01:00
Re: Hi
";
assert_eq!(records.to_string(), expected);
}
#[test]
fn a_body_keeps_its_lines_and_an_empty_one_is_omitted() {
let mut records = Records::default();
let mut snippet = envelope("1", &[], "a@example.org", "first\n\nthird");
snippet.fields = Fields::default();
records.push(snippet);
records.push(envelope("2", &[], "b@example.org", ""));
let expected = "\
1
first
third
2
From: b@example.org
Date: 2026-11-11 10:00+01:00
";
assert_eq!(records.to_string(), expected);
}
#[test]
fn an_empty_list_prints_nothing() {
assert_eq!(Records::default().to_string(), "");
}
}