diff options
Diffstat (limited to 'src/views/mail.rs')
-rw-r--r-- | src/views/mail.rs | 122 |
1 files changed, 122 insertions, 0 deletions
diff --git a/src/views/mail.rs b/src/views/mail.rs new file mode 100644 index 0000000..93c781a --- /dev/null +++ b/src/views/mail.rs @@ -0,0 +1,122 @@ +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<LinearLayout>, +} + +impl MailView { + + pub fn create_for(database_path: PathBuf, id: String, mailfile: PathBuf, name: String) -> Result<NamedView<Self>> { + 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<Vec<TextView>>| { + 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<Vec<TextView>> { + fn collect_into<'a>(v: &mut Vec<TextView>, pm: &'a ParsedMail) -> Result<()> { + v.push(pm.get_body().map(TextView::new)?); + + pm.subparts + .iter() + .map(|subp| collect_into(v, subp)) + .collect::<Result<Vec<_>>>() + .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<usize>) { + self.view.layout(xy) + } + + fn needs_relayout(&self) -> bool { + self.view.needs_relayout() + } + + fn required_size(&mut self, constraint: XY<usize>) -> XY<usize> { + 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<usize>) -> Rect { + self.view.important_area(view_size) + } + + fn type_name(&self) -> &'static str { + self.view.type_name() + } + +} |