summaryrefslogtreecommitdiffstats
path: root/src/views/mail.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/views/mail.rs')
-rw-r--r--src/views/mail.rs122
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()
+ }
+
+}