diff --git a/CHANGELOG.md b/CHANGELOG.md index 00ff51333..2c8c6dcfc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,7 @@ ## Unreleased ### Changes -- refactorings #3354 +- refactorings #3354 #3347 ### Fixes - do not unnecessarily SELECT folders if there are no operations planned on diff --git a/deltachat-ffi/src/lib.rs b/deltachat-ffi/src/lib.rs index 7d34720ee..cbf1ed3f3 100644 --- a/deltachat-ffi/src/lib.rs +++ b/deltachat-ffi/src/lib.rs @@ -458,7 +458,37 @@ pub unsafe extern "C" fn dc_event_get_id(event: *mut dc_event_t) -> libc::c_int } let event = &*event; - event.as_id() + match event.typ { + EventType::Info(_) => 100, + EventType::SmtpConnected(_) => 101, + EventType::ImapConnected(_) => 102, + EventType::SmtpMessageSent(_) => 103, + EventType::ImapMessageDeleted(_) => 104, + EventType::ImapMessageMoved(_) => 105, + EventType::NewBlobFile(_) => 150, + EventType::DeletedBlobFile(_) => 151, + EventType::Warning(_) => 300, + EventType::Error(_) => 400, + EventType::ErrorSelfNotInGroup(_) => 410, + EventType::MsgsChanged { .. } => 2000, + EventType::IncomingMsg { .. } => 2005, + EventType::MsgsNoticed { .. } => 2008, + EventType::MsgDelivered { .. } => 2010, + EventType::MsgFailed { .. } => 2012, + EventType::MsgRead { .. } => 2015, + EventType::ChatModified(_) => 2020, + EventType::ChatEphemeralTimerModified { .. } => 2021, + EventType::ContactsChanged(_) => 2030, + EventType::LocationChanged(_) => 2035, + EventType::ConfigureProgress { .. } => 2041, + EventType::ImexProgress(_) => 2051, + EventType::ImexFileWritten(_) => 2052, + EventType::SecurejoinInviterProgress { .. } => 2060, + EventType::SecurejoinJoinerProgress { .. } => 2061, + EventType::ConnectivityChanged => 2100, + EventType::SelfavatarChanged => 2110, + EventType::WebxdcStatusUpdate { .. } => 2120, + } } #[no_mangle] diff --git a/src/accounts.rs b/src/accounts.rs index 0d49da0f2..f9de647df 100644 --- a/src/accounts.rs +++ b/src/accounts.rs @@ -141,9 +141,10 @@ impl Accounts { /// Remove an account. pub async fn remove_account(&mut self, id: u32) -> Result<()> { - let ctx = self.accounts.remove(&id); - ensure!(ctx.is_some(), "no account with this id: {}", id); - let ctx = ctx.unwrap(); + let ctx = self + .accounts + .remove(&id) + .with_context(|| format!("no account with id {}", id))?; ctx.stop_io().await; drop(ctx); diff --git a/src/blob.rs b/src/blob.rs index 276ac897e..3f20a98d0 100644 --- a/src/blob.rs +++ b/src/blob.rs @@ -289,7 +289,7 @@ impl<'a> BlobObject<'a> { /// Returns the filename of the blob. pub fn as_file_name(&self) -> &str { - self.name.rsplit('/').next().unwrap() + self.name.rsplit('/').next().unwrap_or_default() } /// The path relative in the blob directory. diff --git a/src/dc_receive_imf.rs b/src/dc_receive_imf.rs index 09bbf4023..b55f55923 100644 --- a/src/dc_receive_imf.rs +++ b/src/dc_receive_imf.rs @@ -1441,7 +1441,9 @@ async fn create_or_lookup_group( return Ok(None); } - let grpname = mime_parser.get_header(HeaderDef::ChatGroupName).unwrap(); + let grpname = mime_parser + .get_header(HeaderDef::ChatGroupName) + .context("Chat-Group-Name vanished")?; let new_chat_id = ChatId::create_multiuser_record( context, Chattype::Group, diff --git a/src/events.rs b/src/events.rs index 73ae3a49b..0f158f3bf 100644 --- a/src/events.rs +++ b/src/events.rs @@ -1,10 +1,7 @@ //! # Events specification. -use std::ops::Deref; - use async_std::channel::{self, Receiver, Sender, TrySendError}; use async_std::path::PathBuf; -use strum::EnumProperty; use crate::chat::ChatId; use crate::contact::ContactId; @@ -92,8 +89,6 @@ impl async_std::stream::Stream for EventEmitter { /// context emits them in relation to various operations happening, a lot of these are again /// documented in `deltachat.h`. /// -/// This struct [`Deref`]s to the [`EventType`]. -/// /// [`Context`]: crate::context::Context #[derive(Debug, Clone, PartialEq, Eq)] pub struct Event { @@ -110,68 +105,39 @@ pub struct Event { pub typ: EventType, } -impl Deref for Event { - type Target = EventType; - - fn deref(&self) -> &EventType { - &self.typ - } -} - -impl EventType { - /// Returns the corresponding Event ID. - /// - /// These are the IDs used in the `DC_EVENT_*` constants in `deltachat.h`. - pub fn as_id(&self) -> i32 { - self.get_str("id") - .expect("missing id") - .parse() - .expect("invalid id") - } -} - -#[derive(Debug, Clone, PartialEq, Eq, EnumProperty)] +#[derive(Debug, Clone, PartialEq, Eq)] pub enum EventType { /// The library-user may write an informational string to the log. /// /// This event should *not* be reported to the end-user using a popup or something like /// that. - #[strum(props(id = "100"))] Info(String), /// Emitted when SMTP connection is established and login was successful. - #[strum(props(id = "101"))] SmtpConnected(String), /// Emitted when IMAP connection is established and login was successful. - #[strum(props(id = "102"))] ImapConnected(String), /// Emitted when a message was successfully sent to the SMTP server. - #[strum(props(id = "103"))] SmtpMessageSent(String), /// Emitted when an IMAP message has been marked as deleted - #[strum(props(id = "104"))] ImapMessageDeleted(String), /// Emitted when an IMAP message has been moved - #[strum(props(id = "105"))] ImapMessageMoved(String), /// Emitted when an new file in the $BLOBDIR was created - #[strum(props(id = "150"))] NewBlobFile(String), /// Emitted when an file in the $BLOBDIR was deleted - #[strum(props(id = "151"))] DeletedBlobFile(String), /// The library-user should write a warning string to the log. /// /// This event should *not* be reported to the end-user using a popup or something like /// that. - #[strum(props(id = "300"))] Warning(String), /// The library-user should report an error to the end-user. @@ -184,7 +150,6 @@ pub enum EventType { /// it might be better to delay showing these events until the function has really /// failed (returned false). It should be sufficient to report only the *last* error /// in a messasge box then. - #[strum(props(id = "400"))] Error(String), /// An action cannot be performed because the user is not in the group. @@ -192,7 +157,6 @@ pub enum EventType { /// dc_set_chat_name(), dc_set_chat_profile_image(), /// dc_add_contact_to_chat(), dc_remove_contact_from_chat(), /// dc_send_text_msg() or another sending function. - #[strum(props(id = "410"))] ErrorSelfNotInGroup(String), /// Messages or chats changed. One or more messages or chats changed for various @@ -203,35 +167,44 @@ pub enum EventType { /// /// `chat_id` is set if only a single chat is affected by the changes, otherwise 0. /// `msg_id` is set if only a single message is affected by the changes, otherwise 0. - #[strum(props(id = "2000"))] - MsgsChanged { chat_id: ChatId, msg_id: MsgId }, + MsgsChanged { + chat_id: ChatId, + msg_id: MsgId, + }, /// There is a fresh message. Typically, the user will show an notification /// when receiving this message. /// /// There is no extra #DC_EVENT_MSGS_CHANGED event send together with this event. - #[strum(props(id = "2005"))] - IncomingMsg { chat_id: ChatId, msg_id: MsgId }, + IncomingMsg { + chat_id: ChatId, + msg_id: MsgId, + }, /// Messages were seen or noticed. /// chat id is always set. - #[strum(props(id = "2008"))] MsgsNoticed(ChatId), /// A single message is sent successfully. State changed from DC_STATE_OUT_PENDING to /// DC_STATE_OUT_DELIVERED, see dc_msg_get_state(). - #[strum(props(id = "2010"))] - MsgDelivered { chat_id: ChatId, msg_id: MsgId }, + MsgDelivered { + chat_id: ChatId, + msg_id: MsgId, + }, /// A single message could not be sent. State changed from DC_STATE_OUT_PENDING or DC_STATE_OUT_DELIVERED to /// DC_STATE_OUT_FAILED, see dc_msg_get_state(). - #[strum(props(id = "2012"))] - MsgFailed { chat_id: ChatId, msg_id: MsgId }, + MsgFailed { + chat_id: ChatId, + msg_id: MsgId, + }, /// A single message is read by the receiver. State changed from DC_STATE_OUT_DELIVERED to /// DC_STATE_OUT_MDN_RCVD, see dc_msg_get_state(). - #[strum(props(id = "2015"))] - MsgRead { chat_id: ChatId, msg_id: MsgId }, + MsgRead { + chat_id: ChatId, + msg_id: MsgId, + }, /// Chat changed. The name or the image of a chat group was changed or members were added or removed. /// Or the verify state of a chat has changed. @@ -240,11 +213,9 @@ pub enum EventType { /// /// This event does not include ephemeral timer modification, which /// is a separate event. - #[strum(props(id = "2020"))] ChatModified(ChatId), /// Chat ephemeral timer changed. - #[strum(props(id = "2021"))] ChatEphemeralTimerModified { chat_id: ChatId, timer: EphemeralTimer, @@ -253,7 +224,6 @@ pub enum EventType { /// Contact(s) created, renamed, blocked or deleted. /// /// @param data1 (int) If set, this is the contact_id of an added contact that should be selected. - #[strum(props(id = "2030"))] ContactsChanged(Option), /// Location of one or more contact has changed. @@ -261,11 +231,9 @@ pub enum EventType { /// @param data1 (u32) contact_id of the contact for which the location has changed. /// If the locations of several contacts have been changed, /// eg. after calling dc_delete_all_locations(), this parameter is set to `None`. - #[strum(props(id = "2035"))] LocationChanged(Option), /// Inform about the configuration progress started by configure(). - #[strum(props(id = "2041"))] ConfigureProgress { /// Progress. /// @@ -280,7 +248,6 @@ pub enum EventType { /// /// @param data1 (usize) 0=error, 1-999=progress in permille, 1000=success and done /// @param data2 0 - #[strum(props(id = "2051"))] ImexProgress(usize), /// A file has been exported. A file has been written by imex(). @@ -290,7 +257,6 @@ pub enum EventType { /// services. /// /// @param data2 0 - #[strum(props(id = "2052"))] ImexFileWritten(PathBuf), /// Progress information of a secure-join handshake from the view of the inviter @@ -305,7 +271,6 @@ pub enum EventType { /// 600=vg-/vc-request-with-auth received, vg-member-added/vc-contact-confirm sent, typically shown as "bob@addr verified". /// 800=vg-member-added-received received, shown as "bob@addr securely joined GROUP", only sent for the verified-group-protocol. /// 1000=Protocol finished for this contact. - #[strum(props(id = "2060"))] SecurejoinInviterProgress { contact_id: ContactId, progress: usize, @@ -319,7 +284,6 @@ pub enum EventType { /// @param data2 (int) Progress as: /// 400=vg-/vc-request-with-auth sent, typically shown as "alice@addr verified, introducing myself." /// (Bob has verified alice and waits until Alice does the same for him) - #[strum(props(id = "2061"))] SecurejoinJoinerProgress { contact_id: ContactId, progress: usize, @@ -329,13 +293,10 @@ pub enum EventType { /// This means that you should refresh the connectivity view /// and possibly the connectivtiy HTML; see dc_get_connectivity() and /// dc_get_connectivity_html() for details. - #[strum(props(id = "2100"))] ConnectivityChanged, - #[strum(props(id = "2110"))] SelfavatarChanged, - #[strum(props(id = "2120"))] WebxdcStatusUpdate { msg_id: MsgId, status_update_serial: StatusUpdateSerial, diff --git a/src/html.rs b/src/html.rs index 709821dd5..b5ccca284 100644 --- a/src/html.rs +++ b/src/html.rs @@ -11,7 +11,7 @@ use futures::future::FutureExt; use std::future::Future; use std::pin::Pin; -use anyhow::Result; +use anyhow::{Context as _, Result}; use lettre_email::mime::{self, Mime}; use crate::headerdef::{HeaderDef, HeaderDefMap}; @@ -134,7 +134,7 @@ impl HtmlMsgParser { if raw.is_empty() { return Ok(()); } - let mail = mailparse::parse_mail(&raw).unwrap(); + let mail = mailparse::parse_mail(&raw).context("failed to parse mail")?; self.collect_texts_recursive(context, &mail).await } MimeMultipartType::Single => { @@ -190,7 +190,7 @@ impl HtmlMsgParser { if raw.is_empty() { return Ok(()); } - let mail = mailparse::parse_mail(&raw).unwrap(); + let mail = mailparse::parse_mail(&raw).context("failed to parse mail")?; self.cid_to_data_recursive(context, &mail).await } MimeMultipartType::Single => { diff --git a/src/key.rs b/src/key.rs index 602db31b7..a0296118d 100644 --- a/src/key.rs +++ b/src/key.rs @@ -4,7 +4,7 @@ use std::collections::BTreeMap; use std::fmt; use std::io::Cursor; -use anyhow::{format_err, Context as _, Result}; +use anyhow::{ensure, Context as _, Result}; use async_trait::async_trait; use num_traits::FromPrimitive; use pgp::composed::Deserializable; @@ -82,7 +82,7 @@ pub trait DcKey: Serialize + Deserializable + KeyTrait + Clone { /// The fingerprint for the key. fn fingerprint(&self) -> Fingerprint { - Fingerprint::new(KeyTrait::fingerprint(self)).expect("Invalid fingerprint from rpgp") + Fingerprint::new(KeyTrait::fingerprint(self)) } } @@ -320,11 +320,9 @@ pub async fn store_self_keypair( pub struct Fingerprint(Vec); impl Fingerprint { - pub fn new(v: Vec) -> Result { - match v.len() { - 20 => Ok(Fingerprint(v)), - _ => Err(format_err!("Wrong fingerprint length")), - } + pub fn new(v: Vec) -> Fingerprint { + debug_assert_eq!(v.len(), 20); + Fingerprint(v) } /// Make a hex string from the fingerprint. @@ -364,14 +362,15 @@ impl fmt::Display for Fingerprint { impl std::str::FromStr for Fingerprint { type Err = anyhow::Error; - fn from_str(input: &str) -> std::result::Result { + fn from_str(input: &str) -> Result { let hex_repr: String = input .to_uppercase() .chars() .filter(|&c| ('0'..='9').contains(&c) || ('A'..='F').contains(&c)) .collect(); - let v: Vec = hex::decode(hex_repr)?; - let fp = Fingerprint::new(v)?; + let v: Vec = hex::decode(&hex_repr)?; + ensure!(v.len() == 20, "wrong fingerprint length: {}", hex_repr); + let fp = Fingerprint::new(v); Ok(fp) } } @@ -589,8 +588,7 @@ i8pcjGO+IZffvyZJVRWfVooBJmWWbPB1pueo3tx8w3+fcuzpxz+RLFKaPyqXO+dD fn test_fingerprint_from_str() { let res = Fingerprint::new(vec![ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, - ]) - .unwrap(); + ]); let fp: Fingerprint = "0102030405060708090A0B0c0d0e0F1011121314".parse().unwrap(); assert_eq!(fp, res); @@ -607,8 +605,7 @@ i8pcjGO+IZffvyZJVRWfVooBJmWWbPB1pueo3tx8w3+fcuzpxz+RLFKaPyqXO+dD fn test_fingerprint_hex() { let fp = Fingerprint::new(vec![ 1, 2, 4, 8, 16, 32, 64, 128, 255, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, - ]) - .unwrap(); + ]); assert_eq!(fp.hex(), "0102040810204080FF0A0B0C0D0E0F1011121314"); } @@ -616,8 +613,7 @@ i8pcjGO+IZffvyZJVRWfVooBJmWWbPB1pueo3tx8w3+fcuzpxz+RLFKaPyqXO+dD fn test_fingerprint_to_string() { let fp = Fingerprint::new(vec![ 1, 2, 4, 8, 16, 32, 64, 128, 255, 1, 2, 4, 8, 16, 32, 64, 128, 255, 19, 20, - ]) - .unwrap(); + ]); assert_eq!( fp.to_string(), "0102 0408 1020 4080 FF01\n0204 0810 2040 80FF 1314" diff --git a/src/lib.rs b/src/lib.rs index 62214f21e..53fa25f28 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -8,7 +8,9 @@ clippy::indexing_slicing, clippy::wildcard_imports, clippy::needless_borrow, - clippy::cast_lossless + clippy::cast_lossless, + clippy::unwrap_used, + clippy::expect_used )] #![allow( clippy::match_bool, diff --git a/src/location.rs b/src/location.rs index 8ca110693..98fe7ba17 100644 --- a/src/location.rs +++ b/src/location.rs @@ -153,12 +153,12 @@ impl Kml { ) { let tag = String::from_utf8_lossy(event.name()).trim().to_lowercase(); if tag == "document" { - if let Some(addr) = event.attributes().find(|attr| { - attr.as_ref() - .map(|a| String::from_utf8_lossy(a.key).trim().to_lowercase() == "addr") - .unwrap_or_default() - }) { - self.addr = addr.unwrap().unescape_and_decode_value(reader).ok(); + if let Some(addr) = event + .attributes() + .filter_map(|a| a.ok()) + .find(|attr| String::from_utf8_lossy(attr.key).trim().to_lowercase() == "addr") + { + self.addr = addr.unescape_and_decode_value(reader).ok(); } } else if tag == "placemark" { self.tag = KmlTag::PLACEMARK; diff --git a/src/login_param.rs b/src/login_param.rs index a83f83f78..46dd41eaa 100644 --- a/src/login_param.rs +++ b/src/login_param.rs @@ -210,7 +210,7 @@ impl LoginParam { let key = format!("{}smtp_certificate_checks", prefix); let smtp_certificate_checks = if let Some(certificate_checks) = sql.get_raw_config_int(key).await? { - num_traits::FromPrimitive::from_i32(certificate_checks).unwrap() + num_traits::FromPrimitive::from_i32(certificate_checks).unwrap_or_default() } else { Default::default() }; diff --git a/src/mimeparser.rs b/src/mimeparser.rs index a168e534c..f757fb47f 100644 --- a/src/mimeparser.rs +++ b/src/mimeparser.rs @@ -5,7 +5,7 @@ use std::future::Future; use std::io::Cursor; use std::pin::Pin; -use anyhow::{bail, Result}; +use anyhow::{bail, Context as _, Result}; use deltachat_derive::{FromSql, ToSql}; use lettre_email::mime::{self, Mime}; use mailparse::{addrparse_header, DispositionType, MailHeader, MailHeaderMap, SingleInfo}; @@ -715,7 +715,7 @@ impl MimeMessage { if raw.is_empty() { return Ok(false); } - let mail = mailparse::parse_mail(&raw).unwrap(); + let mail = mailparse::parse_mail(&raw).context("failed to parse mail")?; self.parse_mime_recursive(context, &mail, is_related).await } diff --git a/src/oauth2.rs b/src/oauth2.rs index 94060764e..f525f8966 100644 --- a/src/oauth2.rs +++ b/src/oauth2.rs @@ -146,8 +146,10 @@ pub async fn dc_get_oauth2_access_token( value = &redirect_uri; } else if value == "$CODE" { value = code; - } else if value == "$REFRESH_TOKEN" && refresh_token.is_some() { - value = refresh_token.as_ref().unwrap(); + } else if value == "$REFRESH_TOKEN" { + if let Some(refresh_token) = refresh_token.as_ref() { + value = refresh_token; + } } post_param.insert(key, value); @@ -162,16 +164,18 @@ pub async fn dc_get_oauth2_access_token( let client = surf::Client::new(); let parsed: Result = client.recv_json(req).await; - if parsed.is_err() { - warn!( - context, - "Failed to parse OAuth2 JSON response from {}: error: {:?}", token_url, parsed - ); - return Ok(None); - } + let response = match parsed { + Ok(response) => response, + Err(err) => { + warn!( + context, + "Failed to parse OAuth2 JSON response from {}: error: {}", token_url, err + ); + return Ok(None); + } + }; // update refresh_token if given, typically on the first round, but we update it later as well. - let response = parsed.unwrap(); if let Some(ref token) = response.refresh_token { context .sql @@ -286,12 +290,13 @@ impl Oauth2 { // } let response: Result, surf::Error> = surf::get(userinfo_url).recv_json().await; - if response.is_err() { - warn!(context, "Error getting userinfo: {:?}", response); - return None; - } - - let parsed = response.unwrap(); + let parsed = match response { + Ok(parsed) => parsed, + Err(err) => { + warn!(context, "Error getting userinfo: {}", err); + return None; + } + }; // CAVE: serde_json::Value.as_str() removes the quotes of json-strings // but serde_json::Value.to_string() does not! if let Some(addr) = parsed.get("email") { diff --git a/src/pgp.rs b/src/pgp.rs index 373994e47..73812b927 100644 --- a/src/pgp.rs +++ b/src/pgp.rs @@ -4,7 +4,7 @@ use std::collections::{BTreeMap, HashSet}; use std::io; use std::io::Cursor; -use anyhow::{bail, ensure, format_err, Context as _, Result}; +use anyhow::{bail, format_err, Context as _, Result}; use pgp::armor::BlockType; use pgp::composed::{ Deserializable, KeyType as PgpKeyType, Message, SecretKeyParamsBuilder, SignedPublicKey, @@ -98,9 +98,7 @@ pub fn split_armored_data(buf: &[u8]) -> Result<(BlockType, BTreeMap Result<()> { Ok(mut dir_handle) => { /* avoid deletion of files that are just created to build a message object */ let diff = std::time::Duration::from_secs(60 * 60); - let keep_files_newer_than = std::time::SystemTime::now().checked_sub(diff).unwrap(); + let keep_files_newer_than = std::time::SystemTime::now() + .checked_sub(diff) + .unwrap_or(std::time::SystemTime::UNIX_EPOCH); while let Some(entry) = dir_handle.next().await { - if entry.is_err() { - break; - } - let entry = entry.unwrap(); + let entry = match entry { + Ok(entry) => entry, + Err(_) => break, + }; let name_f = entry.file_name(); let name_s = name_f.to_string_lossy(); @@ -724,11 +726,13 @@ pub async fn remove_unused_files(context: &Context) -> Result<()> { if let Ok(stats) = async_std::fs::metadata(entry.path()).await { let recently_created = - stats.created().is_ok() && stats.created().unwrap() > keep_files_newer_than; - let recently_modified = stats.modified().is_ok() - && stats.modified().unwrap() > keep_files_newer_than; - let recently_accessed = stats.accessed().is_ok() - && stats.accessed().unwrap() > keep_files_newer_than; + stats.created().map_or(false, |t| t > keep_files_newer_than); + let recently_modified = stats + .modified() + .map_or(false, |t| t > keep_files_newer_than); + let recently_accessed = stats + .accessed() + .map_or(false, |t| t > keep_files_newer_than); if recently_created || recently_modified || recently_accessed { info!(