1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
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()
}
}
|