use std::path::PathBuf; use anyhow::Result; use anyhow::Error; use cursive::Printer; use cursive::Rect; use cursive::View; use cursive::XY; use cursive::direction::Direction; use cursive::event::Event; use cursive::event::EventResult; use cursive::view::Nameable; use cursive::view::Selector; use cursive::view::Scrollable; use cursive::views::NamedView; use cursive::views::TextView; use cursive::views::LinearLayout; use cursive::views::ScrollView; use result_inspect::ResultInspect; use mailparse::ParsedMail; pub struct MailView { view: ScrollView, } impl MailView { pub fn create_for(database_path: PathBuf, id: String, mailfile: PathBuf, name: String) -> Result> { let query = format!("id:{}", id); let view = notmuch::Database::open(&database_path, notmuch::DatabaseMode::ReadOnly)? .create_query(&query)? .search_messages()? .map(|msg| { debug!("Constructing textview for '{}'", msg.filename().display()); let buf = std::fs::read(msg.filename())?; debug!("Found {} bytes from {}", buf.len(), msg.filename().display()); let parsed = mailparse::parse_mail(buf)?; MailView::parsed_mail_to_list_of_textviews(&parsed) }) .fold(Ok(LinearLayout::vertical()), |ll: Result<_>, views: Result>| { ll.and_then(|mut l| { views?.into_iter().for_each(|e| l.add_child(e)); Ok(l) }) })?; let view = if view.len() == 0 { debug!("Falling back to mailfile parsing"); LinearLayout::vertical().child({ std::fs::read(&mailfile) .map_err(Error::from) .and_then(|b| String::from_utf8(b).map_err(Error::from)) .inspect(|s| debug!("Found {} bytes from {}", s.bytes().len(), mailfile.display())) .map(TextView::new)? }) } else { view }; Ok(MailView { view: view.scrollable() }.with_name(name)) } fn parsed_mail_to_list_of_textviews<'a>(pm: &'a ParsedMail) -> Result> { fn collect_into<'a>(v: &mut Vec, pm: &'a ParsedMail) -> Result<()> { v.push(pm.get_body().map(TextView::new)?); pm.subparts .iter() .map(|subp| collect_into(v, subp)) .collect::>>() .map(|_| ()) } let mut vec = Vec::new(); collect_into(&mut vec, pm).map(|_| vec) } } impl View for MailView { fn draw(&self, printer: &Printer) { self.view.draw(printer) } fn layout(&mut self, xy: XY) { self.view.layout(xy) } fn needs_relayout(&self) -> bool { self.view.needs_relayout() } fn required_size(&mut self, constraint: XY) -> XY { self.view.required_size(constraint) } fn on_event(&mut self, e: Event) -> EventResult { self.view.on_event(e) } fn call_on_any<'a>(&mut self, s: &Selector, tpl: &'a mut (dyn FnMut(&mut (dyn View + 'static)) + 'a)) { self.view.call_on_any(s, tpl); } fn focus_view(&mut self, s: &Selector) -> Result<(), ()> { self.view.focus_view(s) } fn take_focus(&mut self, source: Direction) -> bool { self.view.take_focus(source) } fn important_area(&self, view_size: XY) -> Rect { self.view.important_area(view_size) } fn type_name(&self) -> &'static str { self.view.type_name() } }