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
parent
d44a68ec69
commit
68c40a2920
|
@ -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),
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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,
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue