melib: return Result with error when an IO operation fails

Don't unwrap anything because this might be temporary, for example a
short IMAP disconnection.
embed
Manos Pitsidianakis 2019-09-27 13:18:59 +03:00
parent d44a68ec69
commit 68c40a2920
Signed by: Manos Pitsidianakis
GPG Key ID: 73627C2F690DF710
7 changed files with 149 additions and 53 deletions

View File

@ -376,16 +376,18 @@ impl Envelope {
let _strings: Vec<String> = self.to.iter().map(|a| format!("{}", a)).collect();
_strings.join(", ")
}
pub fn bytes(&self, mut operation: Box<dyn BackendOp>) -> Vec<u8> {
operation
.as_bytes()
.map(|v| v.into())
.unwrap_or_else(|_| Vec::new())
/// Requests bytes from backend and thus can fail
pub fn bytes(&self, mut operation: Box<dyn BackendOp>) -> Result<Vec<u8>> {
operation.as_bytes().map(|v| v.to_vec())
}
pub fn body_bytes(&self, bytes: &[u8]) -> Attachment {
let builder = AttachmentBuilder::new(bytes);
builder.build()
}
/// Requests bytes from backend and thus can fail
pub fn headers<'a>(&self, bytes: &'a [u8]) -> Result<Vec<(&'a str, &'a str)>> {
let ret = parser::headers(bytes).to_full_result()?;
let len = ret.len();
@ -397,34 +399,14 @@ impl Envelope {
})
})
}
pub fn body(&self, mut operation: Box<dyn BackendOp>) -> Attachment {
/// Requests bytes from backend and thus can fail
pub fn body(&self, mut operation: Box<dyn BackendOp>) -> Result<Attachment> {
debug!("searching body for {:?}", self.message_id_display());
let file = operation.as_bytes();
self.body_bytes(file.unwrap())
/*
let (headers, body) = match parser::mail(file.unwrap()).to_full_result() {
Ok(v) => v,
Err(_) => {
debug!("2error in parsing mail\n");
let error_msg = b"Mail cannot be shown because of errors.";
let mut builder = AttachmentBuilder::new(error_msg);
return builder.build();
}
};
let mut builder = AttachmentBuilder::new(body);
for (name, value) in headers {
if value.len() == 1 && value.is_empty() {
continue;
}
if name.eq_ignore_ascii_case(b"content-transfer-encoding") {
builder.content_transfer_encoding(value);
} else if name.eq_ignore_ascii_case(b"content-type") {
builder.content_type(value);
}
}
builder.build()
*/
let file = operation.as_bytes()?;
Ok(self.body_bytes(file))
}
pub fn subject(&self) -> Cow<str> {
match self.subject {
Some(ref s) => String::from_utf8_lossy(s),

View File

@ -113,7 +113,7 @@ impl str::FromStr for Draft {
}
impl Draft {
pub fn edit(envelope: &Envelope, mut op: Box<dyn BackendOp>) -> Self {
pub fn edit(envelope: &Envelope, mut op: Box<dyn BackendOp>) -> Result<Self> {
let mut ret = Draft::default();
//TODO: Inform user if error
{
@ -128,10 +128,11 @@ impl Draft {
}
}
ret.body = envelope.body(op).text();
ret.body = envelope.body(op)?.text();
ret
Ok(ret)
}
pub fn set_header(&mut self, header: &str, value: String) {
if self.headers.insert(header.to_string(), value).is_none() {
self.header_order.push(header.to_string());

View File

@ -128,15 +128,15 @@ impl Composer {
* msg: index of message we reply to in thread_nodes
* context: current context
*/
pub fn edit(account_pos: usize, h: EnvelopeHash, context: &Context) -> Self {
pub fn edit(account_pos: usize, h: EnvelopeHash, context: &Context) -> Result<Self> {
let mut ret = Composer::default();
let op = context.accounts[account_pos].operation(h);
let envelope: &Envelope = context.accounts[account_pos].get_env(&h);
ret.draft = Draft::edit(envelope, op);
ret.draft = Draft::edit(envelope, op)?;
ret.account_cursor = account_pos;
ret
Ok(ret)
}
pub fn with_context(
coordinates: (usize, usize, usize),

View File

@ -352,6 +352,7 @@ impl ListingTrait for CompactListing {
}
self.filter_term.clear();
let mut error: Option<(EnvelopeHash, MeliError)> = None;
for (i, h) in self.order.keys().enumerate() {
let account = &context.accounts[self.cursor_pos.0];
let envelope = &account.collection[h];
@ -367,8 +368,14 @@ impl ListingTrait for CompactListing {
self.selection.insert(*h, false);
continue;
}
let op = account.operation(*h);
let body = envelope.body(op);
let op = account.operation(env_hash);
let body = match envelope.body(op) {
Ok(b) => b,
Err(e) => {
error = Some((env_hash, e));
break;
}
};
let decoded = decode_rec(&body, None);
let body_text = String::from_utf8_lossy(&decoded);
if body_text.contains(&filter_term) {
@ -377,22 +384,45 @@ impl ListingTrait for CompactListing {
self.selection.insert(*h, false);
}
}
if !self.filtered_selection.is_empty() {
self.filter_term = filter_term.to_string();
self.cursor_pos.2 = std::cmp::min(self.filtered_selection.len() - 1, self.cursor_pos.2);
self.length = self.filtered_selection.len();
} else {
if let Some((env_hash, error)) = error {
self.length = 0;
let message = format!("No results for `{}`.", filter_term);
self.data_columns.columns[0] =
CellBuffer::new(message.len(), self.length + 1, Cell::with_char(' '));
let message = format!("Error: {}", error.to_string());
log(
format!(
"Failed to open envelope {}: {}",
context.accounts[self.new_cursor_pos.0]
.get_env(&env_hash)
.message_id_display(),
error.to_string()
),
ERROR,
);
self.data_columns.columns[0] = CellBuffer::new(message.len(), 1, Cell::with_char(' '));
write_string_to_grid(
&message,
&mut self.data_columns.columns[0],
Color::Default,
Color::Default,
Attr::Default,
((0, 0), (MAX_COLS - 1, 0)),
((0, 0), (message.len() - 1, 0)),
false,
);
} else if !self.filtered_selection.is_empty() {
self.filter_term = filter_term.to_string();
self.cursor_pos.2 = std::cmp::min(self.filtered_selection.len() - 1, self.cursor_pos.2);
self.length = self.filtered_selection.len();
} else {
self.length = 0;
let message = format!("No results for `{}`.", filter_term);
self.data_columns.columns[0] = CellBuffer::new(message.len(), 1, Cell::with_char(' '));
write_string_to_grid(
&message,
&mut self.data_columns.columns[0],
Color::Default,
Color::Default,
Attr::Default,
((0, 0), (message.len() - 1, 0)),
false,
);
}

View File

@ -417,7 +417,13 @@ impl ListingTrait for ConversationsListing {
continue;
}
let op = account.operation(env_hash);
let body = envelope.body(op);
let body = match envelope.body(op) {
Ok(b) => b,
Err(e) => {
error = Some(e);
break;
}
};
let decoded = decode_rec(&body, None);
let body_text = String::from_utf8_lossy(&decoded);
if body_text.contains(&filter_term) {

View File

@ -597,7 +597,25 @@ impl Component for MailView {
let account = &mut context.accounts[self.coordinates.0];
let envelope: &Envelope = &account.get_env(&self.coordinates.2);
let op = account.operation(envelope.hash());
envelope.body(op)
match envelope.body(op) {
Ok(body) => body,
Err(e) => {
context.replies.push_back(UIEvent::Notification(
Some("Failed to open e-mail".to_string()),
e.to_string(),
Some(NotificationType::ERROR),
));
log(
format!(
"Failed to open envelope {}: {}",
envelope.message_id_display(),
e.to_string()
),
ERROR,
);
return;
}
}
};
match self.mode {
ViewMode::Attachment(aidx) if body.attachments()[aidx].is_html() => {
@ -799,7 +817,27 @@ impl Component for MailView {
let account = &mut context.accounts[self.coordinates.0];
let envelope: &Envelope = &account.get_env(&self.coordinates.2);
let op = account.operation(envelope.hash());
if let Some(u) = envelope.body(op).attachments().get(lidx) {
let attachments = match envelope.body(op) {
Ok(body) => body.attachments(),
Err(e) => {
context.replies.push_back(UIEvent::Notification(
Some("Failed to open e-mail".to_string()),
e.to_string(),
Some(NotificationType::ERROR),
));
log(
format!(
"Failed to open envelope {}: {}",
envelope.message_id_display(),
e.to_string()
),
ERROR,
);
return true;
}
};
if let Some(u) = attachments.get(lidx) {
match u.content_type() {
ContentType::MessageRfc822 => {
match EnvelopeWrapper::new(u.body().to_vec()) {
@ -910,7 +948,25 @@ impl Component for MailView {
let envelope: &Envelope = &account.get_env(&self.coordinates.2);
let finder = LinkFinder::new();
let op = account.operation(envelope.hash());
let t = envelope.body(op).text().to_string();
let t = match envelope.body(op) {
Ok(body) => body.text().to_string(),
Err(e) => {
context.replies.push_back(UIEvent::Notification(
Some("Failed to open e-mail".to_string()),
e.to_string(),
Some(NotificationType::ERROR),
));
log(
format!(
"Failed to open envelope {}: {}",
envelope.message_id_display(),
e.to_string()
),
ERROR,
);
return true;
}
};
let links: Vec<Link> = finder.links(&t).collect();
if let Some(u) = links.get(lidx) {
u.as_str().to_string()

View File

@ -1455,7 +1455,28 @@ impl Component for Tabbed {
return true;
}
UIEvent::Action(Tab(Edit(account_pos, msg))) => {
self.add_component(Box::new(Composer::edit(account_pos, msg, context)));
let composer = match Composer::edit(account_pos, msg, context) {
Ok(c) => c,
Err(e) => {
context.replies.push_back(UIEvent::Notification(
Some("Failed to open e-mail".to_string()),
e.to_string(),
Some(NotificationType::ERROR),
));
log(
format!(
"Failed to open envelope {}: {}",
context.accounts[account_pos]
.get_env(&msg)
.message_id_display(),
e.to_string()
),
ERROR,
);
return true;
}
};
self.add_component(Box::new(composer));
self.cursor_pos = self.children.len() - 1;
self.children[self.cursor_pos].set_dirty();
return true;