use super::{Deposit, Epitaph};
pub fn render(ui: &mut egui::Ui, deposits: &[Deposit]) {
egui::ScrollArea::vertical().show(ui, |ui| {
if deposits.is_empty() {
ui.label("(no deposits)");
return;
}
for deposit in deposits {
render_deposit(ui, deposit);
ui.separator();
}
});
}
fn render_deposit(ui: &mut egui::Ui, deposit: &Deposit) {
let from = deposit.sender.as_deref().unwrap_or("?");
let at = deposit.deposited_at.as_deref().unwrap_or("?");
ui.label(
egui::RichText::new(format!("✉ {from} · {at}"))
.color(crate::theme::BRAZEN)
.strong(),
);
if let Some(epitaph) = &deposit.epitaph {
ui.weak(format!("epitaph: {}", epitaph_label(epitaph)));
}
if let Some(terminal) = &deposit.terminal_ref {
ui.weak(format!("terminal: {terminal}"));
}
ui.label(&deposit.body);
}
fn epitaph_label(epitaph: &Epitaph) -> String {
match epitaph {
Epitaph::FinalResponse => "final-response".to_string(),
Epitaph::Stopped => "stopped".to_string(),
Epitaph::BudgetExhausted => "budget-exhausted".to_string(),
Epitaph::Died => "died".to_string(),
Epitaph::Unknown(value) => value.clone(),
}
}
#[cfg(test)]
mod tests {
use super::*;
fn screen() -> egui::RawInput {
egui::RawInput {
screen_rect: Some(egui::Rect::from_min_size(
egui::Pos2::ZERO,
egui::vec2(1024.0, 4096.0),
)),
..Default::default()
}
}
fn collect_text(shape: &egui::Shape, out: &mut String) {
match shape {
egui::Shape::Text(t) => {
out.push_str(t.galley.text());
out.push('\n');
}
egui::Shape::Vec(shapes) => shapes.iter().for_each(|s| collect_text(s, out)),
_ => {}
}
}
fn painted(deposits: &[Deposit]) -> String {
let ctx = egui::Context::default();
let out = ctx.run(screen(), |ctx| {
egui::CentralPanel::default().show(ctx, |ui| render(ui, deposits));
});
let mut text = String::new();
for clipped in out.shapes {
collect_text(&clipped.shape, &mut text);
}
text
}
#[test]
fn empty_inbox_shows_placeholder() {
assert!(painted(&[]).contains("(no deposits)"));
}
#[test]
fn ordinary_deposit_renders_header_and_body() {
let d = Deposit {
sender: Some("user".into()),
deposited_at: Some("2026-07-17T12:00:00Z".into()),
epitaph: None,
terminal_ref: None,
body: "please continue".into(),
};
let text = painted(std::slice::from_ref(&d));
assert!(
text.contains("✉ user · 2026-07-17T12:00:00Z"),
"got:\n{text}"
);
assert!(text.contains("please continue"));
assert!(!text.contains("epitaph:"));
}
#[test]
fn result_message_renders_every_epitaph_and_terminal_ref() {
let cases = [
(Epitaph::FinalResponse, "final-response"),
(Epitaph::Stopped, "stopped"),
(Epitaph::BudgetExhausted, "budget-exhausted"),
(Epitaph::Died, "died"),
(Epitaph::Unknown("wat".into()), "wat"),
];
for (epitaph, label) in cases {
let d = Deposit {
sender: Some("c-1".into()),
deposited_at: Some("t".into()),
epitaph: Some(epitaph),
terminal_ref: Some("deadbeef".into()),
body: String::new(),
};
let text = painted(std::slice::from_ref(&d));
assert!(text.contains(&format!("epitaph: {label}")), "got:\n{text}");
assert!(text.contains("terminal: deadbeef"));
}
}
#[test]
fn absent_fields_render_as_question_marks() {
let d = Deposit {
sender: None,
deposited_at: None,
epitaph: None,
terminal_ref: None,
body: "raw".into(),
};
let text = painted(std::slice::from_ref(&d));
assert!(text.contains("✉ ? · ?"), "got:\n{text}");
}
}