diff --git a/Makefile b/Makefile
index b4406806..96fbbf5d 100644
--- a/Makefile
+++ b/Makefile
@@ -18,6 +18,12 @@
# along with meli. If not, see .
.POSIX:
.SUFFIXES:
+CARGO_TARGET_DIR ?= target
+MIN_RUSTC ?= 1.65.0
+CARGO_BIN ?= cargo
+CARGO_ARGS ?=
+CARGO_SORT_BIN = cargo-sort
+PRINTF = /usr/bin/printf
# Options
PREFIX ?= /usr/local
@@ -25,11 +31,6 @@ EXPANDED_PREFIX := `cd ${PREFIX} && pwd -P`
BINDIR ?= ${EXPANDED_PREFIX}/bin
MANDIR ?= ${EXPANDED_PREFIX}/share/man
-CARGO_TARGET_DIR ?= target
-MIN_RUSTC ?= 1.39.0
-CARGO_BIN ?= cargo
-CARGO_ARGS ?=
-
# Installation parameters
DOCS_SUBDIR ?= docs/
MANPAGES ?= meli.1 meli.conf.5 meli-themes.5
@@ -96,6 +97,15 @@ help:
check:
@${CARGO_BIN} check ${CARGO_ARGS} ${CARGO_COLOR}--target-dir="${CARGO_TARGET_DIR}" ${FEATURES} --all --tests --examples --benches --bins
+.PHONY: fmt
+fmt:
+ @$(CARGO_BIN) +nightly fmt --all || $(CARGO_BIN) fmt --all
+ @OUT=$$($(CARGO_SORT_BIN) -w 2>&1) || $(PRINTF) "WARN: %s cargo-sort failed or binary not found in PATH.\n" "$$OUT"
+
+.PHONY: lint
+lint:
+ @$(CARGO_BIN) clippy --no-deps --all-features --all --tests --examples --benches --bins
+
.PHONY: test
test:
@${CARGO_BIN} test ${CARGO_ARGS} ${CARGO_COLOR}--target-dir="${CARGO_TARGET_DIR}" --all --tests --examples --benches --bins
diff --git a/melib/build.rs b/melib/build.rs
index 659e6ebd..56b4a166 100644
--- a/melib/build.rs
+++ b/melib/build.rs
@@ -19,6 +19,8 @@
* along with meli. If not, see .
*/
+#![allow(clippy::needless_range_loop)]
+
#[cfg(feature = "unicode_algorithms")]
include!("src/text_processing/types.rs");
diff --git a/melib/src/addressbook.rs b/melib/src/addressbook.rs
index 427b8968..6fbf19b8 100644
--- a/melib/src/addressbook.rs
+++ b/melib/src/addressbook.rs
@@ -41,22 +41,37 @@ pub enum CardId {
Hash(u64),
}
-impl Into for CardId {
- fn into(self) -> String {
+impl std::fmt::Display for CardId {
+ fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
match self {
- CardId::Uuid(u) => u.to_string(),
- CardId::Hash(u) => u.to_string(),
+ Self::Uuid(u) => u.as_hyphenated().fmt(fmt),
+ Self::Hash(u) => u.fmt(fmt),
}
}
}
+impl From for String {
+ fn from(val: CardId) -> Self {
+ val.to_string()
+ }
+}
+
impl From for CardId {
- fn from(s: String) -> CardId {
- if let Ok(u) = uuid::Uuid::parse_str(s.as_str()) {
- CardId::Uuid(u)
+ fn from(s: String) -> Self {
+ use std::{
+ collections::hash_map::DefaultHasher,
+ hash::{Hash, Hasher},
+ str::FromStr,
+ };
+
+ if let Ok(u) = Uuid::parse_str(s.as_str()) {
+ Self::Uuid(u)
+ } else if let Ok(num) = u64::from_str(s.trim()) {
+ Self::Hash(num)
} else {
- use std::str::FromStr;
- CardId::Hash(u64::from_str(&s).unwrap())
+ let mut hasher = DefaultHasher::default();
+ s.hash(&mut hasher);
+ Self::Hash(hasher.finish())
}
}
}
@@ -93,8 +108,8 @@ pub struct Card {
}
impl AddressBook {
- pub fn new(display_name: String) -> AddressBook {
- AddressBook {
+ pub fn new(display_name: String) -> Self {
+ Self {
display_name,
created: datetime::now(),
last_edited: datetime::now(),
@@ -102,8 +117,8 @@ impl AddressBook {
}
}
- pub fn with_account(s: &crate::conf::AccountSettings) -> AddressBook {
- let mut ret = AddressBook::new(s.name.clone());
+ pub fn with_account(s: &crate::conf::AccountSettings) -> Self {
+ let mut ret = Self::new(s.name.clone());
if let Some(mutt_alias_file) = s.extra.get("mutt_alias_file").map(String::as_str) {
match std::fs::read_to_string(std::path::Path::new(mutt_alias_file))
.map_err(|err| err.to_string())
@@ -171,8 +186,8 @@ impl Deref for AddressBook {
}
impl Card {
- pub fn new() -> Card {
- Card {
+ pub fn new() -> Self {
+ Self {
id: CardId::Uuid(Uuid::new_v4()),
title: String::new(),
name: String::new(),
@@ -293,8 +308,8 @@ impl Card {
}
impl From> for Card {
- fn from(mut map: HashMap) -> Card {
- let mut card = Card::new();
+ fn from(mut map: HashMap) -> Self {
+ let mut card = Self::new();
if let Some(val) = map.remove("TITLE") {
card.title = val;
}
diff --git a/melib/src/addressbook/vcard.rs b/melib/src/addressbook/vcard.rs
index fdfb0294..20fae379 100644
--- a/melib/src/addressbook/vcard.rs
+++ b/melib/src/addressbook/vcard.rs
@@ -84,7 +84,7 @@ pub struct ContentLine {
}
impl CardDeserializer {
- pub fn from_str(mut input: &str) -> Result> {
+ pub fn try_from_str(mut input: &str) -> Result> {
input = if (!input.starts_with(HEADER_CRLF) || !input.ends_with(FOOTER_CRLF))
&& (!input.starts_with(HEADER_LF) || !input.ends_with(FOOTER_LF))
{
@@ -270,7 +270,7 @@ fn test_load_cards() {
for s in parse_card().parse(contents.as_str()).unwrap().1 {
println!("");
println!("{}", s);
- println!("{:?}", CardDeserializer::from_str(s));
+ println!("{:?}", CardDeserializer::try_from_str(s));
println!("");
}
*/
@@ -295,7 +295,7 @@ pub fn load_cards(p: &std::path::Path) -> Result> {
Ok((_, c)) => {
for s in c {
ret.push(
- CardDeserializer::from_str(s)
+ CardDeserializer::try_from_str(s)
.and_then(TryInto::try_into)
.map(|mut card| {
Card::set_external_resource(&mut card, true);
@@ -326,7 +326,13 @@ pub fn load_cards(p: &std::path::Path) -> Result> {
#[test]
fn test_card() {
let j = "BEGIN:VCARD\r\nVERSION:4.0\r\nN:Gump;Forrest;;Mr.;\r\nFN:Forrest Gump\r\nORG:Bubba Gump Shrimp Co.\r\nTITLE:Shrimp Man\r\nPHOTO;MEDIATYPE=image/gif:http://www.example.com/dir_photos/my_photo.gif\r\nTEL;TYPE=work,voice;VALUE=uri:tel:+1-111-555-1212\r\nTEL;TYPE=home,voice;VALUE=uri:tel:+1-404-555-1212\r\nADR;TYPE=WORK;PREF=1;LABEL=\"100 Waters Edge\\nBaytown\\, LA 30314\\nUnited States of America\":;;100 Waters Edge;Baytown;LA;30314;United States of America\r\nADR;TYPE=HOME;LABEL=\"42 Plantation St.\\nBaytown\\, LA 30314\\nUnited States of America\":;;42 Plantation St.;Baytown;LA;30314;United States of America\r\nEMAIL:forrestgump@example.com\r\nREV:20080424T195243Z\r\nx-qq:21588891\r\nEND:VCARD\r\n";
- println!("results = {:#?}", CardDeserializer::from_str(j).unwrap());
+ println!(
+ "results = {:#?}",
+ CardDeserializer::try_from_str(j).unwrap()
+ );
let j = "BEGIN:VCARD\nVERSION:4.0\nN:Gump;Forrest;;Mr.;\nFN:Forrest Gump\nORG:Bubba Gump Shrimp Co.\nTITLE:Shrimp Man\nPHOTO;MEDIATYPE=image/gif:http://www.example.com/dir_photos/my_photo.gif\nTEL;TYPE=work,voice;VALUE=uri:tel:+1-111-555-1212\nTEL;TYPE=home,voice;VALUE=uri:tel:+1-404-555-1212\nADR;TYPE=WORK;PREF=1;LABEL=\"100 Waters Edge\\nBaytown\\, LA 30314\\nUnited States of America\":;;100 Waters Edge;Baytown;LA;30314;United States of America\nADR;TYPE=HOME;LABEL=\"42 Plantation St.\\nBaytown\\, LA 30314\\nUnited States of America\":;;42 Plantation St.;Baytown;LA;30314;United States of America\nEMAIL:forrestgump@example.com\nREV:20080424T195243Z\nx-qq:21588891\nEND:VCARD\n";
- println!("results = {:#?}", CardDeserializer::from_str(j).unwrap());
+ println!(
+ "results = {:#?}",
+ CardDeserializer::try_from_str(j).unwrap()
+ );
}
diff --git a/melib/src/backends.rs b/melib/src/backends.rs
index 89533e00..6c673c90 100644
--- a/melib/src/backends.rs
+++ b/melib/src/backends.rs
@@ -86,6 +86,8 @@ pub type BackendCreator = Box<
) -> Result>,
>;
+pub type BackendValidateConfigFn = Box Result<()>>;
+
/// A hashmap containing all available mail backends.
/// An abstraction over any available backends.
pub struct Backends {
@@ -94,12 +96,12 @@ pub struct Backends {
pub struct Backend {
pub create_fn: Box BackendCreator>,
- pub validate_conf_fn: Box Result<()>>,
+ pub validate_conf_fn: BackendValidateConfigFn,
}
impl Default for Backends {
fn default() -> Self {
- Backends::new()
+ Self::new()
}
}
@@ -149,7 +151,7 @@ pub const NOTMUCH_ERROR_DETAILS: &str = r#"If notmuch is installed but the libra
impl Backends {
pub fn new() -> Self {
- let mut b = Backends {
+ let mut b = Self {
map: HashMap::with_capacity_and_hasher(1, Default::default()),
};
#[cfg(feature = "maildir_backend")]
@@ -272,8 +274,8 @@ pub enum BackendEvent {
}
impl From for BackendEvent {
- fn from(val: Error) -> BackendEvent {
- BackendEvent::Notice {
+ fn from(val: Error) -> Self {
+ Self::Notice {
description: val.summary.to_string(),
content: Some(val.to_string()),
level: LogLevel::ERROR,
@@ -310,9 +312,10 @@ pub struct RefreshEvent {
#[derive(Clone)]
pub struct BackendEventConsumer(Arc);
+
impl BackendEventConsumer {
pub fn new(b: Arc) -> Self {
- BackendEventConsumer(b)
+ Self(b)
}
}
@@ -355,6 +358,7 @@ pub trait MailBackend: ::std::fmt::Debug + Send + Sync {
Ok(Box::pin(async { Ok(()) }))
}
+ #[allow(clippy::type_complexity)]
fn fetch(
&mut self,
mailbox_hash: MailboxHash,
@@ -493,8 +497,9 @@ pub struct ReadOnlyOp {
}
impl ReadOnlyOp {
+ #[allow(clippy::new_ret_no_self)]
pub fn new(op: Box) -> Box {
- Box::new(ReadOnlyOp { op })
+ Box::new(Self { op })
}
}
@@ -507,8 +512,9 @@ impl BackendOp for ReadOnlyOp {
}
}
-#[derive(Debug, Copy, Hash, Eq, Clone, Serialize, Deserialize, PartialEq)]
+#[derive(Default, Debug, Copy, Hash, Eq, Clone, Serialize, Deserialize, PartialEq)]
pub enum SpecialUsageMailbox {
+ #[default]
Normal,
Inbox,
Archive,
@@ -539,28 +545,22 @@ impl std::fmt::Display for SpecialUsageMailbox {
}
}
-impl Default for SpecialUsageMailbox {
- fn default() -> Self {
- SpecialUsageMailbox::Normal
- }
-}
-
impl SpecialUsageMailbox {
- pub fn detect_usage(name: &str) -> Option {
+ pub fn detect_usage(name: &str) -> Option {
if name.eq_ignore_ascii_case("inbox") {
- Some(SpecialUsageMailbox::Inbox)
+ Some(Self::Inbox)
} else if name.eq_ignore_ascii_case("archive") {
- Some(SpecialUsageMailbox::Archive)
+ Some(Self::Archive)
} else if name.eq_ignore_ascii_case("drafts") {
- Some(SpecialUsageMailbox::Drafts)
+ Some(Self::Drafts)
} else if name.eq_ignore_ascii_case("junk") || name.eq_ignore_ascii_case("spam") {
- Some(SpecialUsageMailbox::Junk)
+ Some(Self::Junk)
} else if name.eq_ignore_ascii_case("sent") {
- Some(SpecialUsageMailbox::Sent)
+ Some(Self::Sent)
} else if name.eq_ignore_ascii_case("trash") {
- Some(SpecialUsageMailbox::Trash)
+ Some(Self::Trash)
} else {
- Some(SpecialUsageMailbox::Normal)
+ Some(Self::Normal)
}
}
}
@@ -608,7 +608,7 @@ pub struct MailboxPermissions {
impl Default for MailboxPermissions {
fn default() -> Self {
- MailboxPermissions {
+ Self {
create_messages: false,
remove_messages: false,
set_flags: false,
@@ -635,7 +635,7 @@ pub struct EnvelopeHashBatch {
impl From for EnvelopeHashBatch {
fn from(value: EnvelopeHash) -> Self {
- EnvelopeHashBatch {
+ Self {
first: value,
rest: SmallVec::new(),
}
@@ -649,16 +649,16 @@ impl std::convert::TryFrom<&[EnvelopeHash]> for EnvelopeHashBatch {
if value.is_empty() {
return Err(());
}
- Ok(EnvelopeHashBatch {
+ Ok(Self {
first: value[0],
rest: value[1..].iter().cloned().collect(),
})
}
}
-impl Into> for &EnvelopeHashBatch {
- fn into(self) -> BTreeSet {
- self.iter().collect::>()
+impl From<&EnvelopeHashBatch> for BTreeSet {
+ fn from(val: &EnvelopeHashBatch) -> Self {
+ val.iter().collect()
}
}
@@ -667,6 +667,10 @@ impl EnvelopeHashBatch {
std::iter::once(self.first).chain(self.rest.iter().cloned())
}
+ pub fn is_empty(&self) -> bool {
+ self.len() == 0
+ }
+
pub fn len(&self) -> usize {
1 + self.rest.len()
}
@@ -715,6 +719,10 @@ impl LazyCountSet {
self.not_yet_seen = self.not_yet_seen.saturating_sub(self.set.len() - old_len);
}
+ pub fn is_empty(&self) -> bool {
+ self.len() == 0
+ }
+
#[inline(always)]
pub fn len(&self) -> usize {
self.set.len() + self.not_yet_seen
diff --git a/melib/src/backends/imap.rs b/melib/src/backends/imap.rs
index eb76bc9d..37c5ef06 100644
--- a/melib/src/backends/imap.rs
+++ b/melib/src/backends/imap.rs
@@ -146,7 +146,7 @@ macro_rules! get_conf_val {
#[derive(Debug)]
pub struct UIDStore {
account_hash: AccountHash,
- account_name: Arc,
+ account_name: Arc,
keep_offline_cache: bool,
capabilities: Arc>,
hash_index: Arc>>,
@@ -171,11 +171,11 @@ pub struct UIDStore {
impl UIDStore {
fn new(
account_hash: AccountHash,
- account_name: Arc,
+ account_name: Arc,
event_consumer: BackendEventConsumer,
timeout: Option,
) -> Self {
- UIDStore {
+ Self {
account_hash,
account_name,
keep_offline_cache: false,
@@ -422,7 +422,7 @@ impl MailBackend for ImapType {
.collect());
}
}
- let new_mailboxes = ImapType::imap_mailboxes(&connection).await?;
+ let new_mailboxes = Self::imap_mailboxes(&connection).await?;
let mut mailboxes = uid_store.mailboxes.lock().await;
*mailboxes = new_mailboxes;
/*
@@ -776,9 +776,7 @@ impl MailBackend for ImapType {
}
Err(tag) => {
let hash = TagHash::from_bytes(tag.as_bytes());
- if !tag_lck.contains_key(&hash) {
- tag_lck.insert(hash, tag.to_string());
- }
+ tag_lck.entry(hash).or_insert_with(|| tag.to_string());
cmd.push_str(tag);
cmd.push(' ');
}
@@ -1242,6 +1240,7 @@ impl MailBackend for ImapType {
}
impl ImapType {
+ #[allow(clippy::new_ret_no_self)]
pub fn new(
s: &AccountSettings,
is_subscribed: Box bool + Send + Sync>,
@@ -1302,7 +1301,7 @@ impl ImapType {
timeout,
};
let account_hash = AccountHash::from_bytes(s.name.as_bytes());
- let account_name = Arc::new(s.name.to_string());
+ let account_name = s.name.to_string().into();
let uid_store: Arc = Arc::new(UIDStore {
keep_offline_cache,
..UIDStore::new(
@@ -1319,7 +1318,7 @@ impl ImapType {
uid_store.clone(),
);
- Ok(Box::new(ImapType {
+ Ok(Box::new(Self {
server_conf,
_is_subscribed: Arc::new(IsSubscribedFn(is_subscribed)),
connection: Arc::new(FutureMutex::new(connection)),
@@ -1423,29 +1422,27 @@ impl ImapType {
}
if let Ok(mut mailbox) = protocol_parser::list_mailbox_result(l).map(|(_, v)| v) {
if let Some(parent) = mailbox.parent {
- if mailboxes.contains_key(&parent) {
- mailboxes
- .entry(parent)
- .and_modify(|e| e.children.push(mailbox.hash));
- } else {
+ if let std::collections::hash_map::Entry::Vacant(e) = mailboxes.entry(parent) {
/* Insert dummy parent entry, populating only the children field. Later
* when we encounter the parent entry we will swap its children with
* dummy's */
- mailboxes.insert(
- parent,
- ImapMailbox {
- children: vec![mailbox.hash],
- ..ImapMailbox::default()
- },
- );
+ e.insert(ImapMailbox {
+ children: vec![mailbox.hash],
+ ..ImapMailbox::default()
+ });
+ } else {
+ mailboxes
+ .entry(parent)
+ .and_modify(|e| e.children.push(mailbox.hash));
}
}
- if mailboxes.contains_key(&mailbox.hash) {
+ if let std::collections::hash_map::Entry::Vacant(e) = mailboxes.entry(mailbox.hash)
+ {
+ e.insert(mailbox);
+ } else {
let entry = mailboxes.entry(mailbox.hash).or_default();
std::mem::swap(&mut entry.children, &mut mailbox.children);
*entry = mailbox;
- } else {
- mailboxes.insert(mailbox.hash, mailbox);
}
} else if let Ok(status) = protocol_parser::status_response(l).map(|(_, v)| v) {
if let Some(mailbox_hash) = status.mailbox {
@@ -1828,9 +1825,7 @@ async fn fetch_hlpr(state: &mut FetchState) -> Result> {
}
for f in keywords {
let hash = TagHash::from_bytes(f.as_bytes());
- if !tag_lck.contains_key(&hash) {
- tag_lck.insert(hash, f.to_string());
- }
+ tag_lck.entry(hash).or_insert_with(|| f.to_string());
env.tags_mut().push(hash);
}
}
diff --git a/melib/src/backends/imap/cache.rs b/melib/src/backends/imap/cache.rs
index 36daf95a..df8b224e 100644
--- a/melib/src/backends/imap/cache.rs
+++ b/melib/src/backends/imap/cache.rs
@@ -34,9 +34,9 @@ pub struct ModSequence(pub std::num::NonZeroU64);
impl TryFrom for ModSequence {
type Error = ();
- fn try_from(val: i64) -> std::result::Result {
+ fn try_from(val: i64) -> std::result::Result {
std::num::NonZeroU64::new(val as u64)
- .map(|u| Ok(ModSequence(u)))
+ .map(|u| Ok(Self(u)))
.unwrap_or(Err(()))
}
}
@@ -163,7 +163,7 @@ mod sqlite3_m {
if i == 0 {
return Err(FromSqlError::OutOfRange(0));
}
- Ok(ModSequence::try_from(i).unwrap())
+ Ok(Self::try_from(i).unwrap())
}
}
@@ -172,7 +172,7 @@ mod sqlite3_m {
Ok(Box::new(Self {
connection: sqlite3::open_or_create_db(
&DB_DESCRIPTION,
- Some(uid_store.account_name.as_str()),
+ Some(&uid_store.account_name),
)?,
loaded_mailboxes: BTreeSet::default(),
uid_store,
@@ -195,13 +195,13 @@ mod sqlite3_m {
impl ImapCacheReset for Sqlite3Cache {
fn reset_db(uid_store: &UIDStore) -> Result<()> {
- sqlite3::reset_db(&DB_DESCRIPTION, Some(uid_store.account_name.as_str()))
+ sqlite3::reset_db(&DB_DESCRIPTION, Some(&uid_store.account_name))
}
}
impl ImapCache for Sqlite3Cache {
fn reset(&mut self) -> Result<()> {
- Sqlite3Cache::reset_db(&self.uid_store)
+ Self::reset_db(&self.uid_store)
}
fn mailbox_state(&mut self, mailbox_hash: MailboxHash) -> Result