diff --git a/src/chat.rs b/src/chat.rs index f30714d0f..1d787a1d0 100644 --- a/src/chat.rs +++ b/src/chat.rs @@ -907,11 +907,10 @@ impl Chat { // the whole list of messages referenced may be huge; // only use the oldest and and the parent message - let parent_references = if let Some(n) = parent_references.find(' ') { - &parent_references[0..n] - } else { - &parent_references - }; + let parent_references = parent_references + .find(' ') + .and_then(|n| parent_references.get(..n)) + .unwrap_or(&parent_references); if !parent_references.is_empty() && !parent_rfc724_mid.is_empty() { // angle brackets are added by the mimefactory later diff --git a/src/configure/mod.rs b/src/configure/mod.rs index 7b45e5386..4894ee747 100644 --- a/src/configure/mod.rs +++ b/src/configure/mod.rs @@ -1,7 +1,5 @@ //! Email accounts autoconfiguration process module -#![forbid(clippy::indexing_slicing)] - mod auto_mozilla; mod auto_outlook; mod read_url; diff --git a/src/contact.rs b/src/contact.rs index 48058b9e6..5839a1eec 100644 --- a/src/contact.rs +++ b/src/contact.rs @@ -1,7 +1,5 @@ //! Contacts module -#![forbid(clippy::indexing_slicing)] - use async_std::path::PathBuf; use deltachat_derive::*; use itertools::Itertools; diff --git a/src/dc_receive_imf.rs b/src/dc_receive_imf.rs index 9dacc1a87..19d39c51b 100644 --- a/src/dc_receive_imf.rs +++ b/src/dc_receive_imf.rs @@ -1515,6 +1515,7 @@ async fn create_adhoc_grp_id(context: &Context, member_ids: &[u32]) -> String { hex_hash(&members) } +#[allow(clippy::indexing_slicing)] fn hex_hash(s: impl AsRef) -> String { let bytes = s.as_ref().as_bytes(); let result = Sha256::digest(bytes); @@ -1567,7 +1568,7 @@ async fn search_chat_ids_by_contact_ids( matches = 0; mismatches = 0; } - if matches < contact_ids.len() && contact_id == contact_ids[matches] { + if contact_ids.get(matches) == Some(&contact_id) { matches += 1; } else { mismatches += 1; @@ -1695,10 +1696,11 @@ async fn check_verified_properties( fn set_better_msg(mime_parser: &mut MimeMessage, better_msg: impl AsRef) { let msg = better_msg.as_ref(); - if !msg.is_empty() && !mime_parser.parts.is_empty() { - let part = &mut mime_parser.parts[0]; - if part.typ == Viewtype::Text { - part.msg = msg.to_string(); + if !msg.is_empty() { + if let Some(part) = mime_parser.parts.get_mut(0) { + if part.typ == Viewtype::Text { + part.msg = msg.to_string(); + } } } } diff --git a/src/dc_tools.rs b/src/dc_tools.rs index de0c5f7fb..0568121ca 100644 --- a/src/dc_tools.rs +++ b/src/dc_tools.rs @@ -22,6 +22,7 @@ pub(crate) fn dc_exactly_one_bit_set(v: i32) -> bool { /// Shortens a string to a specified length and adds "[...]" to the /// end of the shortened string. +#[allow(clippy::indexing_slicing)] pub(crate) fn dc_truncate(buf: &str, approx_chars: usize) -> Cow { let ellipse = "[...]"; @@ -54,6 +55,7 @@ const COLORS: [u32; 16] = [ 0xf2_30_30, 0x39_b2_49, 0xbb_24_3b, 0x96_40_78, 0x66_87_4f, 0x30_8a_b9, 0x12_7e_d0, 0xbe_45_0c, ]; +#[allow(clippy::indexing_slicing)] pub(crate) fn dc_str_to_color(s: impl AsRef) -> u32 { let str_lower = s.as_ref().to_lowercase(); let mut checksum = 0; @@ -198,7 +200,7 @@ fn encode_66bits_as_base64(v1: u32, v2: u32, fill: u32) -> String { pub(crate) fn dc_create_outgoing_rfc724_mid(grpid: Option<&str>, from_addr: &str) -> String { let hostname = from_addr .find('@') - .map(|k| &from_addr[k..]) + .and_then(|k| from_addr.get(k..)) .unwrap_or("@nohost"); match grpid { Some(grpid) => format!("Gr.{}.{}{}", grpid, dc_create_id(), hostname), diff --git a/src/e2ee.rs b/src/e2ee.rs index cb90a30ef..7e6a7ce08 100644 --- a/src/e2ee.rs +++ b/src/e2ee.rs @@ -187,24 +187,23 @@ fn get_autocrypt_mime<'a, 'b>(mail: &'a ParsedMail<'b>) -> Result<&'a ParsedMail "Not a multipart/encrypted message: {}", mail.ctype.mimetype ); - ensure!( - mail.subparts.len() == 2, - "Invalid Autocrypt Level 1 Mime Parts" - ); + if let [first_part, second_part] = &mail.subparts[..] { + ensure!( + first_part.ctype.mimetype == "application/pgp-encrypted", + "Invalid Autocrypt Level 1 version part: {:?}", + first_part.ctype, + ); - ensure!( - mail.subparts[0].ctype.mimetype == "application/pgp-encrypted", - "Invalid Autocrypt Level 1 version part: {:?}", - mail.subparts[0].ctype, - ); + ensure!( + second_part.ctype.mimetype == "application/octet-stream", + "Invalid Autocrypt Level 1 encrypted part: {:?}", + second_part.ctype + ); - ensure!( - mail.subparts[1].ctype.mimetype == "application/octet-stream", - "Invalid Autocrypt Level 1 encrypted part: {:?}", - mail.subparts[1].ctype - ); - - Ok(&mail.subparts[1]) + Ok(second_part) + } else { + bail!("Invalid Autocrypt Level 1 Mime Parts") + } } async fn decrypt_if_autocrypt_message<'a>( @@ -267,6 +266,7 @@ async fn decrypt_part( Ok(None) } +#[allow(clippy::indexing_slicing)] fn has_decrypted_pgp_armor(input: &[u8]) -> bool { if let Some(index) = input.iter().position(|b| *b > b' ') { if input.len() - index > 26 { diff --git a/src/imap/mod.rs b/src/imap/mod.rs index 493e8bd0c..cd9779c4a 100644 --- a/src/imap/mod.rs +++ b/src/imap/mod.rs @@ -3,8 +3,6 @@ //! uses [async-email/async-imap](https://github.com/async-email/async-imap) //! to implement connect, fetch, delete functionality with standard IMAP servers. -#![forbid(clippy::indexing_slicing)] - use std::collections::BTreeMap; use async_imap::{ diff --git a/src/imex.rs b/src/imex.rs index 7577a8bd9..ab6a9fc6d 100644 --- a/src/imex.rs +++ b/src/imex.rs @@ -178,10 +178,11 @@ async fn do_initiate_key_transfer(context: &Context) -> Result { /// /// The `passphrase` must be at least 2 characters long. pub async fn render_setup_file(context: &Context, passphrase: &str) -> Result { - ensure!( - passphrase.len() >= 2, - "Passphrase must be at least 2 chars long." - ); + let passphrase_begin = if let Some(passphrase_begin) = passphrase.get(..2) { + passphrase_begin + } else { + bail!("Passphrase must be at least 2 chars long."); + }; let private_key = SignedSecretKey::load_self(context).await?; let ac_headers = match context.get_config_bool(Config::E2eeEnabled).await { false => None, @@ -196,7 +197,7 @@ pub async fn render_setup_file(context: &Context, passphrase: &str) -> Result 0 && duration_ms < 24 * 60 * 60 * 1000 { - let part_mut = &mut self.parts[0]; part_mut.param.set_int(Param::Duration, duration_ms); } } } + + self.parts.push(part_mut); } } @@ -321,12 +322,11 @@ impl MimeMessage { } } if prepend_subject { - let subj = if let Some(n) = subject.find('[') { - &subject[0..n] - } else { - subject - } - .trim(); + let subj = subject + .find('[') + .and_then(|n| subject.get(..n)) + .unwrap_or(subject) + .trim(); if !subj.is_empty() { for part in self.parts.iter_mut() { @@ -384,8 +384,7 @@ impl MimeMessage { Some(AvatarAction::Delete) } else { let mut i = 0; - while i != self.parts.len() { - let part = &mut self.parts[i]; + while let Some(part) = self.parts.get_mut(i) { if let Some(part_filename) = &part.org_filename { if part_filename == &header_value { if let Some(blob) = part.param.get(Param::File) { @@ -770,16 +769,11 @@ impl MimeMessage { } pub fn repl_msg_by_error(&mut self, error_msg: impl AsRef) { - if self.parts.is_empty() { - return; + if let Some(part) = self.parts.first_mut() { + part.typ = Viewtype::Text; + part.msg = format!("[{}]", error_msg.as_ref()); + self.parts.truncate(1); } - - let part = &mut self.parts[0]; - part.typ = Viewtype::Text; - part.msg = format!("[{}]", error_msg.as_ref()); - self.parts.truncate(1); - - assert_eq!(self.parts.len(), 1); } pub fn get_rfc724_mid(&self) -> Option { @@ -830,7 +824,11 @@ impl MimeMessage { report: &mailparse::ParsedMail<'_>, ) -> Result> { // parse as mailheaders - let report_body = report.subparts[1].get_body_raw()?; + let report_body = if let Some(subpart) = report.subparts.get(1) { + subpart.get_body_raw()? + } else { + bail!("Report does not have second MIME part"); + }; let (report_fields, _) = mailparse::parse_headers(&report_body)?; // must be present @@ -908,6 +906,7 @@ impl MimeMessage { /// Some providers like GMX and Yahoo do not send standard NDNs (Non Delivery notifications). /// If you improve heuristics here you might also have to change prefetch_should_download() in imap/mod.rs. /// Also you should add a test in dc_receive_imf.rs (there already are lots of test_parse_ndn_* tests). + #[allow(clippy::indexing_slicing)] async fn heuristically_parse_ndn(&mut self, context: &Context) -> Option<()> { let maybe_ndn = if let Some(from) = self.get(HeaderDef::From_) { let from = from.to_ascii_lowercase(); @@ -966,11 +965,10 @@ impl MimeMessage { if let Some(failure_report) = &self.failure_report { let error = parts.iter().find(|p| p.typ == Viewtype::Text).map(|p| { let msg = &p.msg; - match msg.find("\n--- ") { - Some(footer_start) => &msg[..footer_start], - None => msg, - } - .trim() + msg.find("\n--- ") + .and_then(|footer_start| msg.get(..footer_start)) + .unwrap_or(msg) + .trim() }); message::handle_ndn(context, failure_report, error).await } @@ -1036,6 +1034,7 @@ pub(crate) struct FailureReport { pub failed_recipient: Option, } +#[allow(clippy::indexing_slicing)] pub(crate) fn parse_message_ids(ids: &str) -> Result> { // take care with mailparse::msgidparse() that is pretty untolerant eg. wrt missing `<` or `>` let mut msgids = Vec::new(); diff --git a/src/param.rs b/src/param.rs index ca0eb9686..77169f42c 100644 --- a/src/param.rs +++ b/src/param.rs @@ -171,7 +171,7 @@ impl str::FromStr for Params { let key = key.unwrap_or_default().trim(); let value = value.unwrap_or_default().trim(); - if let Some(key) = Param::from_u8(key.as_bytes()[0]) { + if let Some(key) = key.as_bytes().first().and_then(|key| Param::from_u8(*key)) { inner.insert(key, value.to_string()); } else { bail!("Unknown key: {}", key); diff --git a/src/pgp.rs b/src/pgp.rs index 4c841858d..ecfc5190e 100644 --- a/src/pgp.rs +++ b/src/pgp.rs @@ -365,11 +365,13 @@ pub async fn symm_decrypt( let decryptor = enc_msg.decrypt_with_password(|| passphrase)?; let msgs = decryptor.collect::>>()?; - ensure!(!msgs.is_empty(), "No valid messages found"); - - match msgs[0].get_content()? { - Some(content) => Ok(content), - None => bail!("Decrypted message is empty"), + if let Some(msg) = msgs.first() { + match msg.get_content()? { + Some(content) => Ok(content), + None => bail!("Decrypted message is empty"), + } + } else { + bail!("No valid messages found") } }) .await diff --git a/src/qr.rs b/src/qr.rs index 8509e9f16..962c27f84 100644 --- a/src/qr.rs +++ b/src/qr.rs @@ -68,6 +68,7 @@ pub async fn check_qr(context: &Context, qr: impl AsRef) -> Lot { /// scheme: `OPENPGP4FPR:FINGERPRINT#a=ADDR&n=NAME&i=INVITENUMBER&s=AUTH` /// or: `OPENPGP4FPR:FINGERPRINT#a=ADDR&g=GROUPNAME&x=GROUPID&i=INVITENUMBER&s=AUTH` +#[allow(clippy::indexing_slicing)] async fn decode_openpgp(context: &Context, qr: &str) -> Lot { let payload = &qr[OPENPGP4FPR_SCHEME.len()..]; @@ -187,6 +188,7 @@ async fn decode_openpgp(context: &Context, qr: &str) -> Lot { } /// scheme: `DCACCOUNT:https://example.org/new_email?t=1w_7wDjgjelxeX884x96v3` +#[allow(clippy::indexing_slicing)] fn decode_account(_context: &Context, qr: &str) -> Lot { let payload = &qr[DCACCOUNT_SCHEME.len()..]; @@ -217,6 +219,7 @@ struct CreateAccountResponse { /// take a qr of the type DC_QR_ACCOUNT, parse it's parameters, /// download additional information from the contained url and set the parameters. /// on success, a configure::configure() should be able to log in to the account +#[allow(clippy::indexing_slicing)] pub async fn set_config_from_qr(context: &Context, qr: &str) -> Result<(), Error> { let url_str = &qr[DCACCOUNT_SCHEME.len()..]; @@ -240,6 +243,7 @@ pub async fn set_config_from_qr(context: &Context, qr: &str) -> Result<(), Error /// Extract address for the mailto scheme. /// /// Scheme: `mailto:addr...?subject=...&body=..` +#[allow(clippy::indexing_slicing)] async fn decode_mailto(context: &Context, qr: &str) -> Lot { let payload = &qr[MAILTO_SCHEME.len()..]; @@ -261,6 +265,7 @@ async fn decode_mailto(context: &Context, qr: &str) -> Lot { /// Extract address for the smtp scheme. /// /// Scheme: `SMTP:addr...:subject...:body...` +#[allow(clippy::indexing_slicing)] async fn decode_smtp(context: &Context, qr: &str) -> Lot { let payload = &qr[SMTP_SCHEME.len()..]; @@ -283,6 +288,7 @@ async fn decode_smtp(context: &Context, qr: &str) -> Lot { /// Scheme: `MATMSG:TO:addr...;SUB:subject...;BODY:body...;` /// /// There may or may not be linebreaks after the fields. +#[allow(clippy::indexing_slicing)] async fn decode_matmsg(context: &Context, qr: &str) -> Lot { // Does not work when the text `TO:` is used in subject/body _and_ TO: is not the first field. // we ignore this case. @@ -316,14 +322,15 @@ lazy_static! { /// Extract address for the matmsg scheme. /// /// Scheme: `VCARD:BEGIN\nN:last name;first name;...;\nEMAIL;:addr...; +#[allow(clippy::indexing_slicing)] async fn decode_vcard(context: &Context, qr: &str) -> Lot { let name = VCARD_NAME_RE .captures(qr) - .map(|caps| { - let last_name = &caps[1]; - let first_name = &caps[2]; + .and_then(|caps| { + let last_name = caps.get(1)?.as_str().trim(); + let first_name = caps.get(2)?.as_str().trim(); - format!("{} {}", first_name.trim(), last_name.trim()) + Some(format!("{} {}", first_name, last_name)) }) .unwrap_or_default(); diff --git a/src/scheduler.rs b/src/scheduler.rs index 30aa731e6..3a8440a2a 100644 --- a/src/scheduler.rs +++ b/src/scheduler.rs @@ -1,5 +1,3 @@ -#![warn(clippy::indexing_slicing)] - use async_std::prelude::*; use async_std::sync::{channel, Receiver, Sender}; use async_std::task; diff --git a/src/securejoin.rs b/src/securejoin.rs index 2dd97559a..b0e4cba71 100644 --- a/src/securejoin.rs +++ b/src/securejoin.rs @@ -352,9 +352,8 @@ async fn send_handshake_msg( } async fn chat_id_2_contact_id(context: &Context, contact_chat_id: ChatId) -> u32 { - let contacts = chat::get_chat_contacts(context, contact_chat_id).await; - if contacts.len() == 1 { - contacts[0] + if let [contact_id] = chat::get_chat_contacts(context, contact_chat_id).await[..] { + contact_id } else { 0 } @@ -365,10 +364,8 @@ async fn fingerprint_equals_sender( fingerprint: &Fingerprint, contact_chat_id: ChatId, ) -> bool { - let contacts = chat::get_chat_contacts(context, contact_chat_id).await; - - if contacts.len() == 1 { - if let Ok(contact) = Contact::load_from_db(context, contacts[0]).await { + if let [contact_id] = chat::get_chat_contacts(context, contact_chat_id).await[..] { + if let Ok(contact) = Contact::load_from_db(context, contact_id).await { if let Some(peerstate) = Peerstate::from_addr(context, contact.get_addr()).await { if peerstate.public_key_fingerprint.is_some() && fingerprint == peerstate.public_key_fingerprint.as_ref().unwrap() @@ -426,6 +423,7 @@ pub(crate) enum HandshakeMessage { /// When handle_securejoin_handshake() is called, /// the message is not yet filed in the database; /// this is done by receive_imf() later on as needed. +#[allow(clippy::indexing_slicing)] pub(crate) async fn handle_securejoin_handshake( context: &Context, mime_message: &MimeMessage, diff --git a/src/simplify.rs b/src/simplify.rs index 79d6f6605..f2b4f0cc0 100644 --- a/src/simplify.rs +++ b/src/simplify.rs @@ -6,6 +6,7 @@ // this escapes a bit more than actually needed by delta (eg. also lines as "-- footer"), // but for non-delta-compatibility, that seems to be better. // (to be only compatible with delta, only "[\r\n|\n]-- {0,2}[\r\n|\n]" needs to be replaced) +#[allow(clippy::indexing_slicing)] pub fn escape_message_footer_marks(text: &str) -> String { if text.starts_with("--") { "-\u{200B}-".to_string() + &text[2..].replace("\n--", "\n-\u{200B}-") @@ -15,6 +16,7 @@ pub fn escape_message_footer_marks(text: &str) -> String { } /// Remove standard (RFC 3676, ยง4.3) footer if it is found. +#[allow(clippy::indexing_slicing)] fn remove_message_footer<'a>(lines: &'a [&str]) -> &'a [&'a str] { let mut nearly_standard_footer = None; for (ix, &line) in lines.iter().enumerate() { @@ -41,6 +43,7 @@ fn remove_message_footer<'a>(lines: &'a [&str]) -> &'a [&'a str] { /// Remove nonstandard footer and a boolean indicating whether such /// footer was removed. +#[allow(clippy::indexing_slicing)] fn remove_nonstandard_footer<'a>(lines: &'a [&str]) -> (&'a [&'a str], bool) { for (ix, &line) in lines.iter().enumerate() { if line == "--" @@ -107,6 +110,7 @@ fn skip_forward_header<'a>(lines: &'a [&str]) -> (&'a [&'a str], bool) { } } +#[allow(clippy::indexing_slicing)] fn remove_bottom_quote<'a>(lines: &'a [&str]) -> (&'a [&'a str], bool) { let mut last_quoted_line = None; for (l, line) in lines.iter().enumerate().rev() { @@ -132,6 +136,7 @@ fn remove_bottom_quote<'a>(lines: &'a [&str]) -> (&'a [&'a str], bool) { } } +#[allow(clippy::indexing_slicing)] fn remove_top_quote<'a>(lines: &'a [&str]) -> (&'a [&'a str], bool) { let mut last_quoted_line = None; let mut has_quoted_headline = false; diff --git a/src/smtp/mod.rs b/src/smtp/mod.rs index 9bdc69235..101a12bed 100644 --- a/src/smtp/mod.rs +++ b/src/smtp/mod.rs @@ -1,7 +1,5 @@ //! # SMTP transport module -#![forbid(clippy::indexing_slicing)] - pub mod send; use std::time::{Duration, Instant}; diff --git a/src/sql.rs b/src/sql.rs index 940af6854..5cc708bd5 100644 --- a/src/sql.rs +++ b/src/sql.rs @@ -586,6 +586,7 @@ pub async fn housekeeping(context: &Context) { info!(context, "Housekeeping done.",); } +#[allow(clippy::indexing_slicing)] fn is_file_in_use(files_in_use: &HashSet, namespc_opt: Option<&str>, name: &str) -> bool { let name_to_check = if let Some(namespc) = namespc_opt { let name_len = name.len(); @@ -600,6 +601,7 @@ fn is_file_in_use(files_in_use: &HashSet, namespc_opt: Option<&str>, nam files_in_use.contains(name_to_check) } +#[allow(clippy::indexing_slicing)] // TODO: use str.strip_prefix once it is released in stable fn maybe_add_file(files_in_use: &mut HashSet, file: impl AsRef) { if !file.as_ref().starts_with("$BLOBDIR/") { return;