diff --git a/melib/src/error.rs b/melib/src/error.rs index 4b5886391..945f92522 100644 --- a/melib/src/error.rs +++ b/melib/src/error.rs @@ -37,12 +37,30 @@ pub type Result = result::Result; #[derive(Debug, Copy, PartialEq, Clone)] pub enum ErrorKind { None, + External, Authentication, Bug, Network, Timeout, } +impl fmt::Display for ErrorKind { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + write!( + fmt, + "{}", + match self { + ErrorKind::None => "None", + ErrorKind::External => "External", + ErrorKind::Authentication => "Authentication", + ErrorKind::Bug => "Bug, please report this!", + ErrorKind::Network => "Network", + ErrorKind::Timeout => "Timeout", + } + ) + } +} + impl ErrorKind { pub fn is_network(&self) -> bool { match self { @@ -172,17 +190,7 @@ impl fmt::Display for MeliError { write!(f, "\nCaused by: {}", source)?; } if self.kind != ErrorKind::None { - write!( - f, - "\nKind: {}", - match self.kind { - ErrorKind::None => "None", - ErrorKind::Authentication => "Authentication", - ErrorKind::Bug => "Bug, please report this!", - ErrorKind::Network => "Network", - ErrorKind::Timeout => "Timeout", - } - )?; + write!(f, "\nKind: {}", self.kind)?; } Ok(()) } diff --git a/src/components/mail/compose.rs b/src/components/mail/compose.rs index 3277d5ca1..4ea4747eb 100644 --- a/src/components/mail/compose.rs +++ b/src/components/mail/compose.rs @@ -750,7 +750,7 @@ impl Component for Composer { context.replies.push_back(UIEvent::Notification( Some("Sent.".into()), String::new(), - Some(NotificationType::INFO), + Some(NotificationType::Info), )); context .replies @@ -760,7 +760,7 @@ impl Component for Composer { context.replies.push_back(UIEvent::Notification( None, err.to_string(), - Some(NotificationType::ERROR), + Some(NotificationType::Error(err.kind)), )); save_draft( self.draft.clone().finalise().unwrap().as_bytes(), @@ -869,7 +869,7 @@ impl Component for Composer { context.replies.push_back(UIEvent::Notification( None, err.to_string(), - Some(NotificationType::ERROR), + Some(NotificationType::Error(err.kind)), )); self.set_dirty(true); } else { @@ -980,7 +980,9 @@ impl Component for Composer { "Subprocess has exited with exit code {}", exit_code ), - Some(NotificationType::ERROR), + Some(NotificationType::Error( + melib::error::ErrorKind::External, + )), )); } else if let EmbedStatus::Running(_, f) = embed { let result = f.read_to_string(); @@ -995,12 +997,12 @@ impl Component for Composer { } self.draft = new_draft; } - Err(_) => { + Err(err) => { context.replies.push_back(UIEvent::Notification( - None, - "Could not parse draft headers correctly. The invalid text has been set as the body of your draft".to_string(), - Some(NotificationType::ERROR), - )); + Some("Could not parse draft headers correctly.".to_string()), + format!("{}\nThe invalid text has been set as the body of your draft", &err), + Some(NotificationType::Error(melib::error::ErrorKind::None)), + )); self.draft.set_body(result); self.has_changes = true; } @@ -1013,6 +1015,24 @@ impl Component for Composer { .replies .push_back(UIEvent::ChangeMode(UIMode::Normal)); } + #[cfg(any(target_os = "linux", target_os = "android"))] + Ok(WaitStatus::PtraceEvent(_, _, _)) + | Ok(WaitStatus::PtraceSyscall(_)) => { + drop(embed_guard); + match self.embed.take() { + Some(EmbedStatus::Running(e, f)) + | Some(EmbedStatus::Stopped(e, f)) => { + self.embed = Some(EmbedStatus::Stopped(e, f)); + } + _ => {} + } + self.mode = ViewMode::Edit; + context + .replies + .push_back(UIEvent::ChangeMode(UIMode::Normal)); + self.dirty = true; + return true; + } Ok(WaitStatus::Stopped(_, _)) => { drop(embed_guard); match self.embed.take() { @@ -1035,11 +1055,28 @@ impl Component for Composer { .push_back(UIEvent::EmbedInput((k.clone(), b.to_vec()))); return true; } - e => { + Ok(WaitStatus::Signaled(_, signal, _)) => { + drop(embed_guard); context.replies.push_back(UIEvent::Notification( None, - format!("Subprocess has exited with reason {:?}", e), - Some(NotificationType::ERROR), + format!("Subprocess was killed by {} signal", signal), + Some(NotificationType::Error( + melib::error::ErrorKind::External, + )), + )); + self.embed = None; + self.mode = ViewMode::Edit; + context + .replies + .push_back(UIEvent::ChangeMode(UIMode::Normal)); + } + Err(err) => { + context.replies.push_back(UIEvent::Notification( + Some("Embed editor crashed.".to_string()), + format!("Subprocess has exited with reason {}", &err), + Some(NotificationType::Error( + melib::error::ErrorKind::External, + )), )); drop(embed_guard); self.embed = None; @@ -1083,11 +1120,11 @@ impl Component for Composer { editor_command.to_string() } else { match std::env::var("EDITOR") { - Err(e) => { + Err(err) => { context.replies.push_back(UIEvent::Notification( - Some(e.to_string()), + Some(err.to_string()), "$EDITOR is not set. You can change an envvar's value with setenv or set composing.editor_command setting in your configuration.".to_string(), - Some(NotificationType::ERROR), + Some(NotificationType::Error(melib::error::ErrorKind::None)), )); return true; } @@ -1150,7 +1187,7 @@ impl Component for Composer { context.replies.push_back(UIEvent::Notification( Some(format!("Failed to execute {}: {}", editor, err)), err.to_string(), - Some(NotificationType::ERROR), + Some(NotificationType::Error(melib::error::ErrorKind::External)), )); context.replies.push_back(UIEvent::Fork(ForkType::Finished)); context.restore_input(); @@ -1167,12 +1204,15 @@ impl Component for Composer { } self.draft = new_draft; } - Err(_) => { + Err(err) => { context.replies.push_back(UIEvent::Notification( - None, - "Could not parse draft headers correctly. The invalid text has been set as the body of your draft".to_string(), - Some(NotificationType::ERROR), - )); + Some("Could not parse draft headers correctly.".to_string()), + format!( + "{}\nThe invalid text has been set as the body of your draft", + &err + ), + Some(NotificationType::Error(melib::error::ErrorKind::None)), + )); self.draft.set_body(result); self.has_changes = true; } @@ -1181,125 +1221,127 @@ impl Component for Composer { self.dirty = true; return true; } - UIEvent::Action(ref a) => { - match a { - Action::Compose(ComposeAction::AddAttachmentPipe(ref command)) => { - if command.is_empty() { + UIEvent::Action(ref a) => match a { + Action::Compose(ComposeAction::AddAttachmentPipe(ref command)) => { + if command.is_empty() { + context.replies.push_back(UIEvent::Notification( + None, + format!("pipe command value is invalid: {}", command), + Some(NotificationType::Error(melib::error::ErrorKind::None)), + )); + return false; + } + let f = create_temp_file(&[], None, None, true); + match std::process::Command::new("sh") + .args(&["-c", command]) + .stdin(std::process::Stdio::null()) + .stdout(std::process::Stdio::from(f.file())) + .spawn() + { + Ok(child) => { + let _ = child + .wait_with_output() + .expect("failed to launch command") + .stdout; + let mut attachment = + match melib::email::compose::attachment_from_file(f.path()) { + Ok(a) => a, + Err(err) => { + context.replies.push_back(UIEvent::Notification( + Some("could not add attachment".to_string()), + err.to_string(), + Some(NotificationType::Error( + melib::error::ErrorKind::None, + )), + )); + self.dirty = true; + return true; + } + }; + if let Ok(mime_type) = query_mime_info(f.path()) { + match attachment.content_type { + ContentType::Other { ref mut tag, .. } => { + *tag = mime_type; + } + _ => {} + } + } + self.draft.attachments_mut().push(attachment); + self.dirty = true; + return true; + } + Err(err) => { context.replies.push_back(UIEvent::Notification( None, - format!("pipe command value is invalid: {}", command), - Some(NotificationType::ERROR), + format!("could not execute pipe command {}: {}", command, &err), + Some(NotificationType::Error(melib::error::ErrorKind::External)), )); - return false; - } - let f = create_temp_file(&[], None, None, true); - match std::process::Command::new("sh") - .args(&["-c", command]) - .stdin(std::process::Stdio::null()) - .stdout(std::process::Stdio::from(f.file())) - .spawn() - { - Ok(child) => { - let _ = child - .wait_with_output() - .expect("failed to launch command") - .stdout; - let mut attachment = - match melib::email::attachment_from_file(f.path()) { - Ok(a) => a, - Err(e) => { - context.replies.push_back(UIEvent::Notification( - Some("could not add attachment".to_string()), - e.to_string(), - Some(NotificationType::ERROR), - )); - self.dirty = true; - return true; - } - }; - if let Ok(mime_type) = query_mime_info(f.path()) { - match attachment.content_type { - ContentType::Other { ref mut tag, .. } => { - *tag = mime_type; - } - _ => {} - } - } - self.draft.attachments_mut().push(attachment); - self.dirty = true; - return true; - } - Err(err) => { - context.replies.push_back(UIEvent::Notification( - None, - format!("could not execute pipe command {}: {}", command, err), - Some(NotificationType::ERROR), - )); - return true; - } + return true; } } - Action::Compose(ComposeAction::AddAttachment(ref path)) => { - let mut attachment = match melib::email::attachment_from_file(path) { - Ok(a) => a, - Err(e) => { - context.replies.push_back(UIEvent::Notification( - Some("could not add attachment".to_string()), - e.to_string(), - Some(NotificationType::ERROR), - )); - self.dirty = true; - return true; - } - }; - if let Ok(mime_type) = query_mime_info(path) { - match attachment.content_type { - ContentType::Other { ref mut tag, .. } => { - *tag = mime_type; - } - _ => {} - } - } - self.draft.attachments_mut().push(attachment); - self.dirty = true; - return true; - } - Action::Compose(ComposeAction::RemoveAttachment(idx)) => { - if *idx + 1 > self.draft.attachments().len() { - context.replies.push_back(UIEvent::StatusEvent( - StatusEvent::DisplayMessage( - "attachment with given index does not exist".to_string(), - ), + } + Action::Compose(ComposeAction::AddAttachment(ref path)) => { + let mut attachment = match melib::email::compose::attachment_from_file(path) { + Ok(a) => a, + Err(err) => { + context.replies.push_back(UIEvent::Notification( + Some("could not add attachment".to_string()), + err.to_string(), + Some(NotificationType::Error(melib::error::ErrorKind::None)), )); self.dirty = true; return true; } - self.draft.attachments_mut().remove(*idx); + }; + if let Ok(mime_type) = query_mime_info(path) { + match attachment.content_type { + ContentType::Other { ref mut tag, .. } => { + *tag = mime_type; + } + _ => {} + } + } + self.draft.attachments_mut().push(attachment); + self.dirty = true; + return true; + } + Action::Compose(ComposeAction::RemoveAttachment(idx)) => { + if *idx + 1 > self.draft.attachments().len() { context.replies.push_back(UIEvent::StatusEvent( - StatusEvent::DisplayMessage("attachment removed".to_string()), + StatusEvent::DisplayMessage( + "attachment with given index does not exist".to_string(), + ), )); self.dirty = true; return true; } - Action::Compose(ComposeAction::SaveDraft) => { - save_draft( - self.draft.clone().finalise().unwrap().as_bytes(), - context, - SpecialUsageMailbox::Drafts, - Flag::SEEN | Flag::DRAFT, - self.account_hash, - ); - return true; - } - Action::Compose(ComposeAction::ToggleSign) => { - let is_true = self.sign_mail.is_true(); - self.sign_mail = ToggleFlag::from(!is_true); - self.dirty = true; - return true; - } - _ => {} + self.draft.attachments_mut().remove(*idx); + context + .replies + .push_back(UIEvent::StatusEvent(StatusEvent::DisplayMessage( + "attachment removed".to_string(), + ))); + self.dirty = true; + return true; } - } + Action::Compose(ComposeAction::SaveDraft) => { + save_draft( + self.draft.clone().finalise().unwrap().as_bytes(), + context, + SpecialUsageMailbox::Drafts, + Flag::SEEN | Flag::DRAFT, + self.account_hash, + ); + return true; + } + Action::Compose(ComposeAction::ToggleSign) => { + let is_true = self.sign_mail.is_true(); + self.sign_mail = ToggleFlag::from(!is_true); + self.dirty = true; + return true; + } + _ => {} + }, _ => {} } false @@ -1469,7 +1511,7 @@ pub fn send_draft( context.accounts[&account_hash].name() )), err.to_string(), - Some(NotificationType::ERROR), + Some(NotificationType::Error(err.kind)), )); return Err(err); } @@ -1513,12 +1555,15 @@ pub fn save_draft( ) { match context.accounts[&account_hash].save_special(bytes, mailbox_type, flags) { Err(MeliError { - summary, details, .. + summary, + details, + kind, + .. }) => { context.replies.push_back(UIEvent::Notification( summary.map(|s| s.into()), details.into(), - Some(NotificationType::ERROR), + Some(NotificationType::Error(kind)), )); } Ok(mailbox_hash) => { @@ -1528,7 +1573,7 @@ pub fn save_draft( "Message saved in `{}`", &context.accounts[&account_hash].mailbox_entries[&mailbox_hash].name ), - Some(NotificationType::INFO), + Some(NotificationType::Info), )); } } diff --git a/src/components/mail/listing.rs b/src/components/mail/listing.rs index 0fbece3b3..c6dcbd24e 100644 --- a/src/components/mail/listing.rs +++ b/src/components/mail/listing.rs @@ -929,7 +929,7 @@ impl Component for Listing { context.replies.push_back(UIEvent::Notification( Some("Could not refresh.".to_string()), err.to_string(), - Some(NotificationType::INFO), + Some(NotificationType::Error(err.kind)), )); } } diff --git a/src/components/mail/listing/compact.rs b/src/components/mail/listing/compact.rs index 7a3101416..91d6342c9 100644 --- a/src/components/mail/listing/compact.rs +++ b/src/components/mail/listing/compact.rs @@ -1358,13 +1358,13 @@ impl CompactListing { self.new_cursor_pos.2 = 0; let message = format!( "Encountered an error while searching for `{}`: {}.", - search_term, err + search_term, &err ); log(message.clone(), ERROR); context.replies.push_back(UIEvent::Notification( Some("Could not perform search".to_string()), message, - Some(crate::types::NotificationType::ERROR), + Some(crate::types::NotificationType::Error(err.kind)), )); } } @@ -1710,7 +1710,7 @@ impl Component for CompactListing { context.replies.push_back(UIEvent::Notification( Some("Could not perform search".to_string()), err.to_string(), - Some(crate::types::NotificationType::ERROR), + Some(crate::types::NotificationType::Error(err.kind)), )); } }; @@ -1739,7 +1739,7 @@ impl Component for CompactListing { context.replies.push_back(UIEvent::Notification( Some("Could not perform search".to_string()), err.to_string(), - Some(crate::types::NotificationType::ERROR), + Some(crate::types::NotificationType::Error(err.kind)), )); } }; diff --git a/src/components/mail/listing/conversations.rs b/src/components/mail/listing/conversations.rs index e5a5bd35c..4a342d750 100644 --- a/src/components/mail/listing/conversations.rs +++ b/src/components/mail/listing/conversations.rs @@ -1567,7 +1567,7 @@ impl Component for ConversationsListing { context.replies.push_back(UIEvent::Notification( Some("Could not perform search".to_string()), err.to_string(), - Some(crate::types::NotificationType::ERROR), + Some(crate::types::NotificationType::Error(err.kind)), )); } }; diff --git a/src/components/mail/listing/plain.rs b/src/components/mail/listing/plain.rs index 89eb65e25..7730e653f 100644 --- a/src/components/mail/listing/plain.rs +++ b/src/components/mail/listing/plain.rs @@ -1348,7 +1348,7 @@ impl Component for PlainListing { context.replies.push_back(UIEvent::Notification( Some("Could not perform search".to_string()), err.to_string(), - Some(crate::types::NotificationType::ERROR), + Some(crate::types::NotificationType::Error(err.kind)), )); } }; diff --git a/src/components/mail/pgp.rs b/src/components/mail/pgp.rs index e4a868ea6..264c95846 100644 --- a/src/components/mail/pgp.rs +++ b/src/components/mail/pgp.rs @@ -28,7 +28,7 @@ pub fn verify_signature(a: &Attachment, context: &mut Context) -> Vec { Ok((bytes, sig)) => { let bytes_file = create_temp_file(&bytes, None, None, true); let signature_file = create_temp_file(sig, None, None, true); - if let Ok(gpg) = Command::new( + match Command::new( context .settings .pgp @@ -48,29 +48,35 @@ pub fn verify_signature(a: &Attachment, context: &mut Context) -> Vec { .stderr(Stdio::piped()) .spawn() { - return gpg.wait_with_output().unwrap().stderr; - } else { - context.replies.push_back(UIEvent::Notification( - Some(format!( - "Failed to launch {} to verify PGP signature", - context - .settings - .pgp - .gpg_binary - .as_ref() - .map(String::as_str) - .unwrap_or("gpg2"), - )), - "see meli.conf(5) for configuration setting pgp.gpg_binary".to_string(), - Some(NotificationType::ERROR), - )); + Ok(gpg) => { + return gpg.wait_with_output().unwrap().stderr; + } + Err(err) => { + context.replies.push_back(UIEvent::Notification( + Some(format!( + "Failed to launch {} to verify PGP signature", + context + .settings + .pgp + .gpg_binary + .as_ref() + .map(String::as_str) + .unwrap_or("gpg2"), + )), + format!( + "{}\nsee meli.conf(5) for configuration setting pgp.gpg_binary", + &err + ), + Some(NotificationType::Error(melib::error::ErrorKind::External)), + )); + } } } - Err(e) => { + Err(err) => { context.replies.push_back(UIEvent::Notification( - Some(e.to_string()), - String::new(), - Some(NotificationType::ERROR), + Some("Could not verify signature.".to_string()), + err.to_string(), + Some(NotificationType::Error(err.kind)), )); } } diff --git a/src/components/mail/view.rs b/src/components/mail/view.rs index db6d8026f..c7e697405 100644 --- a/src/components/mail/view.rs +++ b/src/components/mail/view.rs @@ -342,58 +342,60 @@ impl MailView { .stdin(Stdio::piped()) .stdout(Stdio::piped()) .spawn(); - if command_obj.is_err() { - context.replies.push_back(UIEvent::Notification( - Some(format!( - "Failed to start html filter process: {}", - filter_invocation, - )), - String::new(), - Some(NotificationType::ERROR), - )); - return; - } - - let mut html_filter = command_obj.unwrap(); - html_filter - .stdin - .as_mut() - .unwrap() - .write_all(&v) - .expect("Failed to write to stdin"); - *v = format!( + match command_obj { + Err(err) => { + context.replies.push_back(UIEvent::Notification( + Some(format!( + "Failed to start html filter process: {}", + filter_invocation, + )), + err.to_string(), + Some(NotificationType::Error(melib::ErrorKind::External)), + )); + return; + } + Ok(mut html_filter) => { + html_filter + .stdin + .as_mut() + .unwrap() + .write_all(&v) + .expect("Failed to write to stdin"); + *v = format!( "Text piped through `{}`. Press `v` to open in web browser. \n\n", filter_invocation ) - .into_bytes(); - v.extend(html_filter.wait_with_output().unwrap().stdout); + .into_bytes(); + v.extend(html_filter.wait_with_output().unwrap().stdout); + } + } } else { - if let Ok(mut html_filter) = Command::new("w3m") + match Command::new("w3m") .args(&["-I", "utf-8", "-T", "text/html"]) .stdin(Stdio::piped()) .stdout(Stdio::piped()) .spawn() { - html_filter - .stdin - .as_mut() - .unwrap() - .write_all(&v) - .expect("Failed to write to html filter stdin"); - *v = String::from( + Ok(mut html_filter) => { + html_filter + .stdin + .as_mut() + .unwrap() + .write_all(&v) + .expect("Failed to write to html filter stdin"); + *v = String::from( "Text piped through `w3m`. Press `v` to open in web browser. \n\n", ) .into_bytes(); - v.extend(html_filter.wait_with_output().unwrap().stdout); - } else { - context.replies.push_back(UIEvent::Notification( - Some( - "Failed to find any application to use as html filter" - .to_string(), - ), - String::new(), - Some(NotificationType::ERROR), - )); + v.extend(html_filter.wait_with_output().unwrap().stdout); + } + Err(err) => { + context.replies.push_back(UIEvent::Notification( + Some("Failed to launch w3m to use as html filter".to_string()), + err.to_string(), + Some(NotificationType::Error(melib::ErrorKind::External)), + )); + } } } } else if a.is_signed() { @@ -469,7 +471,7 @@ impl MailView { context.replies.push_back(UIEvent::Notification( Some("Failed to open e-mail".to_string()), err.to_string(), - Some(NotificationType::ERROR), + Some(NotificationType::Error(err.kind)), )); log( format!("Failed to open envelope: {}", err.to_string()), @@ -563,7 +565,7 @@ impl MailView { context.replies.push_back(UIEvent::Notification( None, s, - Some(NotificationType::ERROR), + Some(NotificationType::Error(melib::ErrorKind::None)), )); } _ => {} @@ -877,7 +879,7 @@ impl Component for MailView { context.replies.push_back(UIEvent::Notification( Some("Failed to open e-mail".to_string()), err.to_string(), - Some(NotificationType::ERROR), + Some(NotificationType::Error(err.kind)), )); log( format!("Failed to open envelope: {}", err.to_string()), @@ -1219,7 +1221,7 @@ impl Component for MailView { context.replies.push_back(UIEvent::Notification( Some("Failed to open e-mail".to_string()), err_string, - Some(NotificationType::ERROR), + Some(NotificationType::Error(err.kind)), )); } } @@ -1414,7 +1416,7 @@ impl Component for MailView { context.replies.push_back(UIEvent::Notification( Some("Failed to launch xdg-open".to_string()), err.to_string(), - Some(NotificationType::ERROR), + Some(NotificationType::Error(melib::ErrorKind::External)), )); } } @@ -1451,7 +1453,7 @@ impl Component for MailView { context.replies.push_back(UIEvent::Notification( Some("Failed to open e-mail".to_string()), err.to_string(), - Some(NotificationType::ERROR), + Some(NotificationType::Error(err.kind)), )); log( format!("Failed to open envelope: {}", err.to_string()), @@ -1475,7 +1477,7 @@ impl Component for MailView { context.replies.push_back(UIEvent::Notification( Some(format!("Failed to create file at {}", path.display())), err.to_string(), - Some(NotificationType::ERROR), + Some(NotificationType::Error(melib::ErrorKind::External)), )); log( format!( @@ -1501,7 +1503,7 @@ impl Component for MailView { context.replies.push_back(UIEvent::Notification( None, format!("Saved at {}", &path.display()), - Some(NotificationType::INFO), + Some(NotificationType::Info), )); return true; @@ -1515,17 +1517,17 @@ impl Component for MailView { | ContentType::PGPSignature => { debug!(path); let mut f = match std::fs::File::create(path) { - Err(e) => { + Err(err) => { context.replies.push_back(UIEvent::Notification( Some(format!("Failed to create file at {}", path)), - e.to_string(), - Some(NotificationType::ERROR), + err.to_string(), + Some(NotificationType::Error(melib::ErrorKind::External)), )); log( format!( "Failed to create file at {}: {}", path, - e.to_string() + err.to_string() ), ERROR, ); @@ -1557,17 +1559,17 @@ impl Component for MailView { name: ref _name, .. } => { let mut f = match std::fs::File::create(path.trim()) { - Err(e) => { + Err(err) => { context.replies.push_back(UIEvent::Notification( Some(format!("Failed to create file at {}", path)), - e.to_string(), - Some(NotificationType::ERROR), + err.to_string(), + Some(NotificationType::Error(melib::ErrorKind::External)), )); log( format!( "Failed to create file at {}: {}", path, - e.to_string() + err.to_string() ), ERROR, ); @@ -1583,7 +1585,7 @@ impl Component for MailView { context.replies.push_back(UIEvent::Notification( None, format!("Saved at {}", &path), - Some(NotificationType::INFO), + Some(NotificationType::Info), )); } else { context diff --git a/src/components/mail/view/envelope.rs b/src/components/mail/view/envelope.rs index d50ef4acc..5a18f3aff 100644 --- a/src/components/mail/view/envelope.rs +++ b/src/components/mail/view/envelope.rs @@ -101,31 +101,33 @@ impl EnvelopeView { .stdin(Stdio::piped()) .stdout(Stdio::piped()) .spawn(); - if command_obj.is_err() { - context.replies.push_back(UIEvent::Notification( - Some(format!( - "Failed to start html filter process: {}", - filter_invocation, - )), - String::new(), - Some(NotificationType::ERROR), - )); - return; - } - - let mut html_filter = command_obj.unwrap(); - html_filter - .stdin - .as_mut() - .unwrap() - .write_all(&v) - .expect("Failed to write to stdin"); - *v = format!( + match command_obj { + Err(err) => { + context.replies.push_back(UIEvent::Notification( + Some(format!( + "Failed to start html filter process: {}", + filter_invocation, + )), + err.to_string(), + Some(NotificationType::Error(melib::ErrorKind::External)), + )); + return; + } + Ok(mut html_filter) => { + html_filter + .stdin + .as_mut() + .unwrap() + .write_all(&v) + .expect("Failed to write to stdin"); + *v = format!( "Text piped through `{}`. Press `v` to open in web browser. \n\n", filter_invocation ) - .into_bytes(); - v.extend(html_filter.wait_with_output().unwrap().stdout); + .into_bytes(); + v.extend(html_filter.wait_with_output().unwrap().stdout); + } + } } } })), diff --git a/src/components/mail/view/html.rs b/src/components/mail/view/html.rs index ef361f1b4..1bc02e12d 100644 --- a/src/components/mail/view/html.rs +++ b/src/components/mail/view/html.rs @@ -43,32 +43,34 @@ impl HtmlView { .stdin(Stdio::piped()) .stdout(Stdio::piped()) .spawn(); - if command_obj.is_err() { - context.replies.push_back(UIEvent::Notification( - Some(format!( - "Failed to start html filter process: {}", - filter_invocation, - )), - String::new(), - Some(NotificationType::ERROR), - )); - String::from_utf8_lossy(&bytes).to_string() - } else { - let mut html_filter = command_obj.unwrap(); - html_filter - .stdin - .as_mut() - .unwrap() - .write_all(&bytes) - .expect("Failed to write to html filter stdin"); - let mut display_text = format!( - "Text piped through `{}`. Press `v` to open in web browser. \n\n", - filter_invocation - ); - display_text.push_str(&String::from_utf8_lossy( - &html_filter.wait_with_output().unwrap().stdout, - )); - display_text + match command_obj { + Err(err) => { + context.replies.push_back(UIEvent::Notification( + Some(format!( + "Failed to start html filter process: {}", + filter_invocation, + )), + err.to_string(), + Some(NotificationType::Error(melib::ErrorKind::External)), + )); + String::from_utf8_lossy(&bytes).to_string() + } + Ok(mut html_filter) => { + html_filter + .stdin + .as_mut() + .unwrap() + .write_all(&bytes) + .expect("Failed to write to html filter stdin"); + let mut display_text = format!( + "Text piped through `{}`. Press `v` to open in web browser. \n\n", + filter_invocation + ); + display_text.push_str(&String::from_utf8_lossy( + &html_filter.wait_with_output().unwrap().stdout, + )); + display_text + } } } else if let Ok(mut html_filter) = Command::new("w3m") .args(&["-I", "utf-8", "-T", "text/html"]) @@ -93,7 +95,7 @@ impl HtmlView { context.replies.push_back(UIEvent::Notification( Some("Failed to find any application to use as html filter".to_string()), String::new(), - Some(NotificationType::ERROR), + Some(NotificationType::Error(melib::error::ErrorKind::None)), )); String::from_utf8_lossy(&bytes).to_string() }; diff --git a/src/components/notifications.rs b/src/components/notifications.rs index 685252bfd..2384f09a8 100644 --- a/src/components/notifications.rs +++ b/src/components/notifications.rs @@ -71,17 +71,46 @@ mod dbus { let mut notification = notify_rust::Notification::new(); notification .appname("meli") - .icon("mail-message-new") .summary(title.as_ref().map(String::as_str).unwrap_or("meli")) .body(&escape_str(body)); - if *kind == Some(NotificationType::NEWMAIL) { - notification.hint(notify_rust::Hint::Category("email".to_owned())); + match *kind { + Some(NotificationType::NewMail) => { + notification.hint(notify_rust::Hint::Category("email".to_owned())); + notification.icon("mail-message-new"); + notification.sound_name("message-new-email"); + } + Some(NotificationType::SentMail) => { + notification.hint(notify_rust::Hint::Category("email".to_owned())); + notification.icon("mail-send"); + notification.sound_name("message-sent-email"); + } + Some(NotificationType::Saved) => { + notification.icon("document-save"); + } + Some(NotificationType::Info) => { + notification.icon("dialog-information"); + } + Some(NotificationType::Error(melib::ErrorKind::Authentication)) => { + notification.icon("dialog-password"); + } + Some(NotificationType::Error(melib::ErrorKind::Bug)) => { + notification.icon("face-embarrassed"); + } + Some(NotificationType::Error(melib::ErrorKind::None)) + | Some(NotificationType::Error(melib::ErrorKind::External)) => { + notification.icon("dialog-error"); + } + Some(NotificationType::Error(melib::ErrorKind::Network)) => { + notification.icon("network-error"); + } + Some(NotificationType::Error(melib::ErrorKind::Timeout)) => { + notification.icon("network-offline"); + } + _ => {} } if settings.play_sound.is_true() { if let Some(ref sound_path) = settings.sound_file { notification.hint(notify_rust::Hint::SoundFile(sound_path.to_owned())); - } else { - notification.sound_name("message-new-email"); } } else { notification.hint(notify_rust::Hint::SuppressSound(true)); @@ -186,7 +215,7 @@ impl Component for NotificationCommand { } } - if *kind == Some(NotificationType::NEWMAIL) { + if *kind == Some(NotificationType::NewMail) { if let Some(ref path) = context.settings.notifications.xbiff_file_path { if let Err(err) = update_xbiff(path) { debug!("Could not update xbiff file: {:?}", &err); diff --git a/src/conf/accounts.rs b/src/conf/accounts.rs index e60b683af..469b3c597 100644 --- a/src/conf/accounts.rs +++ b/src/conf/accounts.rs @@ -856,7 +856,7 @@ impl Account { self.name, self.mailbox_entries[&mailbox_hash].name() ), - Some(crate::types::NotificationType::NEWMAIL), + Some(crate::types::NotificationType::NewMail), )); } RefreshEventKind::Remove(env_hash) => { @@ -906,7 +906,7 @@ impl Account { .send(ThreadEvent::UIEvent(UIEvent::Notification( Some(format!("{} watcher exited with error", &self.name)), e.to_string(), - Some(crate::types::NotificationType::ERROR), + Some(crate::types::NotificationType::Error(err.kind)), ))) .expect("Could not send event on main channel"); */ @@ -914,7 +914,7 @@ impl Account { return Some(Notification( Some("Account watch failed".into()), err.to_string(), - Some(crate::types::NotificationType::ERROR), + Some(crate::types::NotificationType::Error(err.kind)), )); } } @@ -1303,7 +1303,7 @@ impl Account { .send(ThreadEvent::UIEvent(UIEvent::Notification( None, format!("'`{}` has been subscribed.", &path), - Some(crate::types::NotificationType::INFO), + Some(crate::types::NotificationType::Info), ))) .expect("Could not send event on main channel"); Ok(()) @@ -1323,7 +1323,7 @@ impl Account { .send(ThreadEvent::UIEvent(UIEvent::Notification( None, format!("'`{}` has been unsubscribed.", &path), - Some(crate::types::NotificationType::INFO), + Some(crate::types::NotificationType::Info), ))) .expect("Could not send event on main channel"); Ok(()) @@ -1468,7 +1468,7 @@ impl Account { .send(ThreadEvent::UIEvent(UIEvent::Notification( Some(format!("{}: authentication error", &self.name)), err.to_string(), - Some(crate::types::NotificationType::ERROR), + Some(crate::types::NotificationType::Error(err.kind)), ))) .expect("Could not send event on main channel"); self.is_online = Err(err); @@ -1511,7 +1511,7 @@ impl Account { .send(ThreadEvent::UIEvent(UIEvent::Notification( Some(format!("{}: could not fetch mailbox", &self.name)), err.to_string(), - Some(crate::types::NotificationType::ERROR), + Some(crate::types::NotificationType::Error(err.kind)), ))) .expect("Could not send event on main channel"); self.mailbox_entries @@ -1636,7 +1636,7 @@ impl Account { .send(ThreadEvent::UIEvent(UIEvent::Notification( Some(format!("{}: could not set flag", &self.name)), err.to_string(), - Some(crate::types::NotificationType::ERROR), + Some(crate::types::NotificationType::Error(err.kind)), ))) .expect("Could not send event on main channel"); } @@ -1665,7 +1665,7 @@ impl Account { "Message was stored in {} so that you can restore it manually.", file.path.display() ), - Some(crate::types::NotificationType::INFO), + Some(crate::types::NotificationType::Info), ))) .expect("Could not send event on main channel"); } @@ -1678,7 +1678,7 @@ impl Account { .send(ThreadEvent::UIEvent(UIEvent::Notification( Some("Could not send message".to_string()), err.to_string(), - Some(crate::types::NotificationType::ERROR), + Some(crate::types::NotificationType::Error(err.kind)), ))) .expect("Could not send event on main channel"); } @@ -1694,7 +1694,7 @@ impl Account { .send(ThreadEvent::UIEvent(UIEvent::Notification( Some(format!("{}: could not save message", &self.name)), err.to_string(), - Some(crate::types::NotificationType::ERROR), + Some(crate::types::NotificationType::Error(err.kind)), ))) .expect("Could not send event on main channel"); } @@ -1706,7 +1706,7 @@ impl Account { .send(ThreadEvent::UIEvent(UIEvent::Notification( Some(format!("{}: could not delete message", &self.name)), err.to_string(), - Some(crate::types::NotificationType::ERROR), + Some(crate::types::NotificationType::Error(err.kind)), ))) .expect("Could not send event on main channel"); } @@ -1727,7 +1727,7 @@ impl Account { &self.name, path )), err.to_string(), - Some(crate::types::NotificationType::ERROR), + Some(crate::types::NotificationType::Error(err.kind)), ))) .expect("Could not send event on main channel"); } @@ -1807,7 +1807,7 @@ impl Account { .send(ThreadEvent::UIEvent(UIEvent::Notification( Some(format!("{}: could not delete mailbox", &self.name)), err.to_string(), - Some(crate::types::NotificationType::ERROR), + Some(crate::types::NotificationType::Error(err.kind)), ))) .expect("Could not send event on main channel"); } @@ -1862,7 +1862,7 @@ impl Account { .send(ThreadEvent::UIEvent(UIEvent::Notification( Some(format!("{}: mailbox deleted successfully", &self.name)), String::new(), - Some(crate::types::NotificationType::INFO), + Some(crate::types::NotificationType::Info), ))) .expect("Could not send event on main channel"); } @@ -1882,7 +1882,7 @@ impl Account { &self.name )), err.to_string(), - Some(crate::types::NotificationType::ERROR), + Some(crate::types::NotificationType::Error(err.kind)), ))) .expect("Could not send event on main channel"); } @@ -1894,7 +1894,7 @@ impl Account { &self.name )), String::new(), - Some(crate::types::NotificationType::INFO), + Some(crate::types::NotificationType::Info), ))) .expect("Could not send event on main channel"); } @@ -1912,7 +1912,7 @@ impl Account { &self.name )), err.to_string(), - Some(crate::types::NotificationType::ERROR), + Some(crate::types::NotificationType::Error(err.kind)), ))) .expect("Could not send event on main channel"); } @@ -1924,7 +1924,7 @@ impl Account { &self.name )), String::new(), - Some(crate::types::NotificationType::INFO), + Some(crate::types::NotificationType::Info), ))) .expect("Could not send event on main channel"); } @@ -1947,7 +1947,7 @@ impl Account { .send(ThreadEvent::UIEvent(UIEvent::Notification( Some(format!("{}: watch thread failed", &self.name)), err.to_string(), - Some(crate::types::NotificationType::ERROR), + Some(crate::types::NotificationType::Error(err.kind)), ))) .expect("Could not send event on main channel"); } @@ -1967,7 +1967,7 @@ impl Account { .send(ThreadEvent::UIEvent(UIEvent::Notification( Some(format!("{}: {} failed", &self.name, name,)), err.to_string(), - Some(crate::types::NotificationType::ERROR), + Some(crate::types::NotificationType::Error(err.kind)), ))) .expect("Could not send event on main channel"); } @@ -1977,7 +1977,7 @@ impl Account { .send(ThreadEvent::UIEvent(UIEvent::Notification( Some(format!("{}: {} succeeded", &self.name, name,)), String::new(), - Some(crate::types::NotificationType::INFO), + Some(crate::types::NotificationType::Info), ))) .expect("Could not send event on main channel"); } diff --git a/src/state.rs b/src/state.rs index d0179b60a..c3ab65c5a 100644 --- a/src/state.rs +++ b/src/state.rs @@ -851,7 +851,7 @@ impl State { self.context.replies.push_back(UIEvent::Notification( None, format!("Account {} was not found.", account_name), - Some(NotificationType::ERROR), + Some(NotificationType::Error(ErrorKind::None)), )); return; }; @@ -867,7 +867,7 @@ impl State { "Account {} doesn't have an sqlite3 search backend.", account_name ), - Some(NotificationType::ERROR), + Some(NotificationType::Error(ErrorKind::None)), )); return; } @@ -888,14 +888,14 @@ impl State { self.context.replies.push_back(UIEvent::Notification( None, "Message index rebuild started.".to_string(), - Some(NotificationType::INFO), + Some(NotificationType::Info), )); } - Err(e) => { + Err(err) => { self.context.replies.push_back(UIEvent::Notification( - None, - format!("Message index rebuild failed: {}.", e), - Some(NotificationType::ERROR), + Some("Message index rebuild failed".to_string()), + err.to_string(), + Some(NotificationType::Error(err.kind)), )); } } @@ -906,7 +906,7 @@ impl State { None, "Message index rebuild failed: meli is not built with sqlite3 support." .to_string(), - Some(NotificationType::ERROR), + Some(NotificationType::Error(ErrorKind::None)), )); } AccountAction(ref account_name, PrintAccountSetting(ref setting)) => { @@ -930,7 +930,7 @@ impl State { self.context.replies.push_back(UIEvent::Notification( None, format!("Account {} was not found.", account_name), - Some(NotificationType::ERROR), + Some(NotificationType::Error(ErrorKind::None)), )); return; } diff --git a/src/types.rs b/src/types.rs index 0c1c642d4..1e4e1e580 100644 --- a/src/types.rs +++ b/src/types.rs @@ -91,22 +91,23 @@ pub enum ForkType { #[derive(Debug, PartialEq, Copy, Clone)] pub enum NotificationType { - INFO, - ERROR, - NEWMAIL, + Info, + Error(melib::error::ErrorKind), + NewMail, + SentMail, + Saved, } impl core::fmt::Display for NotificationType { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!( - f, - "{}", - match *self { - NotificationType::INFO => "info", - NotificationType::ERROR => "error", - NotificationType::NEWMAIL => "new-mail", - } - ) + match *self { + NotificationType::Info => write!(f, "info"), + NotificationType::Error(melib::error::ErrorKind::None) => write!(f, "error"), + NotificationType::Error(kind) => write!(f, "error: {}", kind), + NotificationType::NewMail => write!(f, "new mail"), + NotificationType::SentMail => write!(f, "sent mail"), + NotificationType::Saved => write!(f, "saved"), + } } }