diff --git a/melib/src/backends/imap/cache.rs b/melib/src/backends/imap/cache.rs index 5ddb2b68..9ee95665 100644 --- a/melib/src/backends/imap/cache.rs +++ b/melib/src/backends/imap/cache.rs @@ -464,7 +464,7 @@ mod sqlite3_m { max_uid = std::cmp::max(max_uid, *uid); tx.execute( "INSERT OR REPLACE INTO envelopes (hash, uid, mailbox_hash, modsequence, envelope) VALUES (?1, ?2, ?3, ?4, ?5)", - sqlite3::params![envelope.hash() as i64, *uid as Sqlite3UID, mailbox_hash as i64, modseq, &envelope], + sqlite3::params![envelope.hash(), *uid as Sqlite3UID, mailbox_hash as i64, modseq, &envelope], ).chain_err_summary(|| format!("Could not insert envelope {} {} in header_cache of account {}", envelope.message_id(), envelope.hash(), uid_store.account_name))?; } } @@ -586,16 +586,13 @@ mod sqlite3_m { )?; let x = stmt - .query_map( - sqlite3::params![mailbox_hash as i64, env_hash as i64], - |row| { - Ok(( - row.get(0).map(|u: Sqlite3UID| u as UID)?, - row.get(1)?, - row.get(2)?, - )) - }, - )? + .query_map(sqlite3::params![mailbox_hash as i64, env_hash], |row| { + Ok(( + row.get(0).map(|u: Sqlite3UID| u as UID)?, + row.get(1)?, + row.get(2)?, + )) + })? .collect::>()?; x } @@ -635,10 +632,9 @@ mod sqlite3_m { "SELECT rfc822 FROM envelopes WHERE mailbox_hash = ?1 AND hash = ?2;", )?; let x = stmt - .query_map( - sqlite3::params![mailbox_hash as i64, env_hash as i64], - |row| row.get(0), - )? + .query_map(sqlite3::params![mailbox_hash as i64, env_hash], |row| { + row.get(0) + })? .collect::>()?; x } diff --git a/melib/src/backends/imap/protocol_parser.rs b/melib/src/backends/imap/protocol_parser.rs index bb828a3e..eaf2b08f 100644 --- a/melib/src/backends/imap/protocol_parser.rs +++ b/melib/src/backends/imap/protocol_parser.rs @@ -1336,7 +1336,7 @@ pub fn envelope(input: &[u8]) -> IResult<&[u8], Envelope> { Ok(( input, ({ - let mut env = Envelope::new(0); + let mut env = Envelope::new(EnvelopeHash::default()); if let Some(date) = date { env.set_date(&date); if let Ok(d) = @@ -1760,5 +1760,5 @@ pub fn generate_envelope_hash(mailbox_path: &str, uid: &UID) -> EnvelopeHash { let mut h = DefaultHasher::new(); h.write_usize(*uid); h.write(mailbox_path.as_bytes()); - h.finish() + EnvelopeHash(h.finish()) } diff --git a/melib/src/backends/jmap/objects/email.rs b/melib/src/backends/jmap/objects/email.rs index b1e2f3c4..2c83409f 100644 --- a/melib/src/backends/jmap/objects/email.rs +++ b/melib/src/backends/jmap/objects/email.rs @@ -26,9 +26,7 @@ use core::marker::PhantomData; use serde::de::{Deserialize, Deserializer}; use serde_json::value::RawValue; use serde_json::Value; -use std::collections::hash_map::DefaultHasher; use std::collections::HashMap; -use std::hash::Hasher; mod import; pub use import::*; @@ -42,9 +40,7 @@ impl Object for ThreadObject { impl Id { pub fn into_hash(&self) -> EnvelopeHash { - let mut h = DefaultHasher::new(); - h.write(self.inner.as_bytes()); - h.finish() + EnvelopeHash::from_bytes(self.inner.as_bytes()) } } @@ -250,7 +246,7 @@ impl std::fmt::Display for EmailAddress { impl std::convert::From for crate::Envelope { fn from(mut t: EmailObject) -> crate::Envelope { - let mut env = crate::Envelope::new(0); + let mut env = crate::Envelope::new(t.id.into_hash()); if let Ok(d) = crate::email::parser::dates::rfc5322_date(env.date_as_str().as_bytes()) { env.set_datetime(d); } @@ -327,7 +323,6 @@ impl std::convert::From for crate::Envelope { } } - env.set_hash(t.id.into_hash()); env } } diff --git a/melib/src/backends/maildir/backend.rs b/melib/src/backends/maildir/backend.rs index 55b6ca47..a8ac0a55 100644 --- a/melib/src/backends/maildir/backend.rs +++ b/melib/src/backends/maildir/backend.rs @@ -150,7 +150,7 @@ macro_rules! get_path_hash { pub fn get_file_hash(file: &Path) -> EnvelopeHash { let mut hasher = DefaultHasher::default(); file.hash(&mut hasher); - hasher.finish() + EnvelopeHash(hasher.finish()) } pub fn move_to_cur(p: PathBuf) -> Result { diff --git a/melib/src/backends/nntp/protocol_parser.rs b/melib/src/backends/nntp/protocol_parser.rs index c9d78e47..2501288b 100644 --- a/melib/src/backends/nntp/protocol_parser.rs +++ b/melib/src/backends/nntp/protocol_parser.rs @@ -119,7 +119,7 @@ pub fn over_article(input: &str) -> IResult<&str, (UID, Envelope)> { let mut hasher = DefaultHasher::new(); hasher.write(num.as_bytes()); hasher.write(message_id.unwrap_or_default().as_bytes()); - hasher.finish() + EnvelopeHash(hasher.finish()) }; let mut env = Envelope::new(env_hash); if let Some(date) = date { diff --git a/melib/src/backends/notmuch.rs b/melib/src/backends/notmuch.rs index 81d98e62..5fc2331e 100644 --- a/melib/src/backends/notmuch.rs +++ b/melib/src/backends/notmuch.rs @@ -903,8 +903,11 @@ impl MailBackend for NotmuchDb { s.push(' '); s } else { - return Err(MeliError::new("Mailbox with hash {} not found!") - .set_kind(crate::error::ErrorKind::Bug)); + return Err(MeliError::new(format!( + "Mailbox with hash {} not found!", + mailbox_hash + )) + .set_kind(crate::error::ErrorKind::Bug)); } } else { String::new() diff --git a/melib/src/backends/notmuch/message.rs b/melib/src/backends/notmuch/message.rs index 16c5123c..a8104b4d 100644 --- a/melib/src/backends/notmuch/message.rs +++ b/melib/src/backends/notmuch/message.rs @@ -58,11 +58,7 @@ impl<'m> Message<'m> { pub fn env_hash(&self) -> EnvelopeHash { let msg_id = unsafe { call!(self.lib, notmuch_message_get_message_id)(self.message) }; let c_str = unsafe { CStr::from_ptr(msg_id) }; - { - let mut hasher = DefaultHasher::default(); - c_str.hash(&mut hasher); - hasher.finish() - } + EnvelopeHash::from_bytes(c_str.to_bytes_with_nul()) } pub fn header(&self, header: &CStr) -> Option<&[u8]> { diff --git a/melib/src/email.rs b/melib/src/email.rs index 538d8887..75db0231 100644 --- a/melib/src/email.rs +++ b/melib/src/email.rs @@ -198,7 +198,31 @@ impl Mail { } } -pub type EnvelopeHash = u64; +#[derive( + Hash, Eq, PartialEq, Debug, Ord, PartialOrd, Default, Serialize, Deserialize, Copy, Clone, +)] +#[repr(transparent)] +pub struct EnvelopeHash(pub u64); + +impl EnvelopeHash { + #[inline(always)] + pub fn from_bytes(bytes: &[u8]) -> Self { + let mut h = DefaultHasher::new(); + h.write(bytes); + Self(h.finish()) + } + + #[inline(always)] + pub const fn to_be_bytes(self) -> [u8; 8] { + self.0.to_be_bytes() + } +} + +impl core::fmt::Display for EnvelopeHash { + fn fmt(&self, fmt: &mut core::fmt::Formatter) -> core::fmt::Result { + write!(fmt, "{}", self.0) + } +} /// `Envelope` represents all the header and structure data of an email we need to know. /// @@ -244,7 +268,7 @@ impl core::fmt::Debug for Envelope { impl Default for Envelope { fn default() -> Self { - Envelope::new(0) + Envelope::new(EnvelopeHash::default()) } } @@ -276,9 +300,7 @@ impl Envelope { } pub fn from_bytes(bytes: &[u8], flags: Option) -> Result { - let mut h = DefaultHasher::new(); - h.write(bytes); - let mut e = Envelope::new(h.finish()); + let mut e = Envelope::new(EnvelopeHash::from_bytes(bytes)); let res = e.populate_headers(bytes).ok(); if res.is_some() { if let Some(f) = flags { @@ -396,7 +418,7 @@ impl Envelope { } if self.message_id.raw().is_empty() { let hash = self.hash; - self.set_message_id(format!("<{:x}>", hash).as_bytes()); + self.set_message_id(format!("<{:x}>", hash.0).as_bytes()); } if self.references.is_some() { if let Some(pos) = self diff --git a/melib/src/email/compose.rs b/melib/src/email/compose.rs index afecdb4e..1454ece3 100644 --- a/melib/src/email/compose.rs +++ b/melib/src/email/compose.rs @@ -90,7 +90,7 @@ impl FromStr for Draft { ret.headers .insert(k.try_into()?, String::from_utf8(v.to_vec())?); } - let body = Envelope::new(0).body_bytes(s.as_bytes()); + let body = Envelope::new(EnvelopeHash::default()).body_bytes(s.as_bytes()); ret.body = String::from_utf8(body.decode(Default::default()))?; diff --git a/melib/src/sqlite3.rs b/melib/src/sqlite3.rs index 17a7b67f..898454f6 100644 --- a/melib/src/sqlite3.rs +++ b/melib/src/sqlite3.rs @@ -19,7 +19,7 @@ * along with meli. If not, see . */ -use crate::{error::*, logging::log, Envelope}; +use crate::{error::*, logging::log, Envelope, EnvelopeHash}; use rusqlite::types::{FromSql, FromSqlError, FromSqlResult, ToSql, ToSqlOutput}; pub use rusqlite::{self, params, Connection}; use std::path::PathBuf; @@ -159,3 +159,17 @@ impl FromSql for Envelope { .map_err(|e| FromSqlError::Other(Box::new(e))) } } + +impl ToSql for EnvelopeHash { + fn to_sql(&self) -> rusqlite::Result { + Ok(ToSqlOutput::from(self.0 as i64)) + } +} + +impl FromSql for EnvelopeHash { + fn column_result(value: rusqlite::types::ValueRef) -> FromSqlResult { + let b: i64 = FromSql::column_result(value)?; + + Ok(EnvelopeHash(b as u64)) + } +} diff --git a/src/sqlite3.rs b/src/sqlite3.rs index c2ad9659..deecf295 100644 --- a/src/sqlite3.rs +++ b/src/sqlite3.rs @@ -30,17 +30,12 @@ use melib::{ backends::{MailBackend, ResultFuture}, email::{Envelope, EnvelopeHash}, log, - sqlite3::{ - self as melib_sqlite3, - rusqlite::{self, params}, - DatabaseDescription, - }, + sqlite3::{self as melib_sqlite3, rusqlite::params, DatabaseDescription}, thread::{SortField, SortOrder}, MeliError, Result, ERROR, }; use smallvec::SmallVec; -use std::convert::TryInto; use std::path::PathBuf; use std::sync::{Arc, RwLock}; @@ -367,16 +362,9 @@ pub fn search( .map_err(|e| MeliError::new(e.to_string()))?; let results = stmt - .query_map([], |row| row.get(0)) - .map_err(|e| MeliError::new(e.to_string()))? - .map(|r: std::result::Result, rusqlite::Error>| { - Ok(u64::from_be_bytes( - r.map_err(|e| MeliError::new(e.to_string()))? - .as_slice() - .try_into() - .map_err(|e: std::array::TryFromSliceError| MeliError::new(e.to_string()))?, - )) - }) + .query_map([], |row| row.get::<_, EnvelopeHash>(0)) + .map_err(MeliError::from)? + .map(|item| item.map_err(MeliError::from)) .collect::>>(); Ok(Box::pin(async { results })) }