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();
|
let _strings: Vec<String> = self.to.iter().map(|a| format!("{}", a)).collect();
|
||||||
_strings.join(", ")
|
_strings.join(", ")
|
||||||
}
|
}
|
||||||
pub fn bytes(&self, mut operation: Box<dyn BackendOp>) -> Vec<u8> {
|
|
||||||
operation
|
/// Requests bytes from backend and thus can fail
|
||||||
.as_bytes()
|
pub fn bytes(&self, mut operation: Box<dyn BackendOp>) -> Result<Vec<u8>> {
|
||||||
.map(|v| v.into())
|
operation.as_bytes().map(|v| v.to_vec())
|
||||||
.unwrap_or_else(|_| Vec::new())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn body_bytes(&self, bytes: &[u8]) -> Attachment {
|
pub fn body_bytes(&self, bytes: &[u8]) -> Attachment {
|
||||||
let builder = AttachmentBuilder::new(bytes);
|
let builder = AttachmentBuilder::new(bytes);
|
||||||
builder.build()
|
builder.build()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Requests bytes from backend and thus can fail
|
||||||
pub fn headers<'a>(&self, bytes: &'a [u8]) -> Result<Vec<(&'a str, &'a str)>> {
|
pub fn headers<'a>(&self, bytes: &'a [u8]) -> Result<Vec<(&'a str, &'a str)>> {
|
||||||
let ret = parser::headers(bytes).to_full_result()?;
|
let ret = parser::headers(bytes).to_full_result()?;
|
||||||
let len = ret.len();
|
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());
|
debug!("searching body for {:?}", self.message_id_display());
|
||||||
let file = operation.as_bytes();
|
let file = operation.as_bytes()?;
|
||||||
self.body_bytes(file.unwrap())
|
Ok(self.body_bytes(file))
|
||||||
/*
|
|
||||||
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()
|
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn subject(&self) -> Cow<str> {
|
pub fn subject(&self) -> Cow<str> {
|
||||||
match self.subject {
|
match self.subject {
|
||||||
Some(ref s) => String::from_utf8_lossy(s),
|
Some(ref s) => String::from_utf8_lossy(s),
|
||||||
|
|
|
@ -113,7 +113,7 @@ impl str::FromStr for Draft {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl 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();
|
let mut ret = Draft::default();
|
||||||
//TODO: Inform user if error
|
//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) {
|
pub fn set_header(&mut self, header: &str, value: String) {
|
||||||
if self.headers.insert(header.to_string(), value).is_none() {
|
if self.headers.insert(header.to_string(), value).is_none() {
|
||||||
self.header_order.push(header.to_string());
|
self.header_order.push(header.to_string());
|
||||||
|
|
|
@ -128,15 +128,15 @@ impl Composer {
|
||||||
* msg: index of message we reply to in thread_nodes
|
* msg: index of message we reply to in thread_nodes
|
||||||
* context: current context
|
* 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 mut ret = Composer::default();
|
||||||
let op = context.accounts[account_pos].operation(h);
|
let op = context.accounts[account_pos].operation(h);
|
||||||
let envelope: &Envelope = context.accounts[account_pos].get_env(&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.account_cursor = account_pos;
|
||||||
ret
|
Ok(ret)
|
||||||
}
|
}
|
||||||
pub fn with_context(
|
pub fn with_context(
|
||||||
coordinates: (usize, usize, usize),
|
coordinates: (usize, usize, usize),
|
||||||
|
|
|
@ -352,6 +352,7 @@ impl ListingTrait for CompactListing {
|
||||||
}
|
}
|
||||||
self.filter_term.clear();
|
self.filter_term.clear();
|
||||||
|
|
||||||
|
let mut error: Option<(EnvelopeHash, MeliError)> = None;
|
||||||
for (i, h) in self.order.keys().enumerate() {
|
for (i, h) in self.order.keys().enumerate() {
|
||||||
let account = &context.accounts[self.cursor_pos.0];
|
let account = &context.accounts[self.cursor_pos.0];
|
||||||
let envelope = &account.collection[h];
|
let envelope = &account.collection[h];
|
||||||
|
@ -367,8 +368,14 @@ impl ListingTrait for CompactListing {
|
||||||
self.selection.insert(*h, false);
|
self.selection.insert(*h, false);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let op = account.operation(*h);
|
let op = account.operation(env_hash);
|
||||||
let body = envelope.body(op);
|
let body = match envelope.body(op) {
|
||||||
|
Ok(b) => b,
|
||||||
|
Err(e) => {
|
||||||
|
error = Some((env_hash, e));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
};
|
||||||
let decoded = decode_rec(&body, None);
|
let decoded = decode_rec(&body, None);
|
||||||
let body_text = String::from_utf8_lossy(&decoded);
|
let body_text = String::from_utf8_lossy(&decoded);
|
||||||
if body_text.contains(&filter_term) {
|
if body_text.contains(&filter_term) {
|
||||||
|
@ -377,22 +384,45 @@ impl ListingTrait for CompactListing {
|
||||||
self.selection.insert(*h, false);
|
self.selection.insert(*h, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !self.filtered_selection.is_empty() {
|
|
||||||
self.filter_term = filter_term.to_string();
|
if let Some((env_hash, error)) = error {
|
||||||
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;
|
self.length = 0;
|
||||||
let message = format!("No results for `{}`.", filter_term);
|
let message = format!("Error: {}", error.to_string());
|
||||||
self.data_columns.columns[0] =
|
log(
|
||||||
CellBuffer::new(message.len(), self.length + 1, Cell::with_char(' '));
|
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(
|
write_string_to_grid(
|
||||||
&message,
|
&message,
|
||||||
&mut self.data_columns.columns[0],
|
&mut self.data_columns.columns[0],
|
||||||
Color::Default,
|
Color::Default,
|
||||||
Color::Default,
|
Color::Default,
|
||||||
Attr::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,
|
false,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -417,7 +417,13 @@ impl ListingTrait for ConversationsListing {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let op = account.operation(env_hash);
|
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 decoded = decode_rec(&body, None);
|
||||||
let body_text = String::from_utf8_lossy(&decoded);
|
let body_text = String::from_utf8_lossy(&decoded);
|
||||||
if body_text.contains(&filter_term) {
|
if body_text.contains(&filter_term) {
|
||||||
|
|
|
@ -597,7 +597,25 @@ impl Component for MailView {
|
||||||
let account = &mut context.accounts[self.coordinates.0];
|
let account = &mut context.accounts[self.coordinates.0];
|
||||||
let envelope: &Envelope = &account.get_env(&self.coordinates.2);
|
let envelope: &Envelope = &account.get_env(&self.coordinates.2);
|
||||||
let op = account.operation(envelope.hash());
|
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 {
|
match self.mode {
|
||||||
ViewMode::Attachment(aidx) if body.attachments()[aidx].is_html() => {
|
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 account = &mut context.accounts[self.coordinates.0];
|
||||||
let envelope: &Envelope = &account.get_env(&self.coordinates.2);
|
let envelope: &Envelope = &account.get_env(&self.coordinates.2);
|
||||||
let op = account.operation(envelope.hash());
|
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() {
|
match u.content_type() {
|
||||||
ContentType::MessageRfc822 => {
|
ContentType::MessageRfc822 => {
|
||||||
match EnvelopeWrapper::new(u.body().to_vec()) {
|
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 envelope: &Envelope = &account.get_env(&self.coordinates.2);
|
||||||
let finder = LinkFinder::new();
|
let finder = LinkFinder::new();
|
||||||
let op = account.operation(envelope.hash());
|
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();
|
let links: Vec<Link> = finder.links(&t).collect();
|
||||||
if let Some(u) = links.get(lidx) {
|
if let Some(u) = links.get(lidx) {
|
||||||
u.as_str().to_string()
|
u.as_str().to_string()
|
||||||
|
|
|
@ -1455,7 +1455,28 @@ impl Component for Tabbed {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
UIEvent::Action(Tab(Edit(account_pos, msg))) => {
|
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.cursor_pos = self.children.len() - 1;
|
||||||
self.children[self.cursor_pos].set_dirty();
|
self.children[self.cursor_pos].set_dirty();
|
||||||
return true;
|
return true;
|
||||||
|
|
Loading…
Reference in New Issue