From 5699baecfba9cb15aac04a6b400cfb6bc881e2c5 Mon Sep 17 00:00:00 2001 From: Manos Pitsidianakis Date: Sun, 18 Jun 2023 22:28:40 +0300 Subject: [PATCH] melib: add utils::{futures, random} --- melib/src/addressbook.rs | 2 +- melib/src/addressbook/mutt.rs | 2 +- melib/src/addressbook/vcard.rs | 7 +- melib/src/backends/imap.rs | 2 +- melib/src/backends/imap/cache.rs | 6 +- melib/src/backends/imap/connection.rs | 66 +++--- melib/src/backends/imap/search.rs | 4 +- melib/src/backends/jmap.rs | 4 +- melib/src/backends/jmap/objects/email.rs | 12 +- melib/src/backends/maildir.rs | 2 +- melib/src/backends/maildir/backend.rs | 2 +- melib/src/backends/mbox.rs | 2 +- melib/src/backends/mbox/write.rs | 2 +- melib/src/backends/nntp.rs | 9 +- melib/src/backends/nntp/connection.rs | 2 +- melib/src/backends/notmuch.rs | 2 +- melib/src/backends/notmuch/message.rs | 2 +- melib/src/backends/notmuch/thread.rs | 2 +- melib/src/email.rs | 3 +- melib/src/email/compose.rs | 3 +- melib/src/email/compose/random.rs | 26 +-- melib/src/email/mailto.rs | 2 +- melib/src/email/parser.rs | 13 +- melib/src/error.rs | 67 ++++-- melib/src/lib.rs | 13 +- melib/src/search.rs | 11 +- melib/src/sieve.rs | 4 +- melib/src/smtp.rs | 2 +- melib/src/thread.rs | 4 +- melib/src/utils/connections.rs | 21 -- melib/src/utils/futures.rs | 43 ++++ melib/src/utils/logging.rs | 4 +- melib/src/utils/mod.rs | 2 + melib/src/utils/parsec.rs | 11 +- melib/src/utils/random.rs | 71 +++++++ src/command.rs | 208 ++++++++++--------- src/components/mail/compose.rs | 2 +- src/components/mail/compose/hooks.rs | 22 +- src/components/mail/listing/conversations.rs | 2 +- src/components/mail/listing/plain.rs | 2 +- src/components/mail/view.rs | 4 +- src/components/svg.rs | 4 +- src/conf.rs | 2 +- src/conf/accounts.rs | 18 +- src/sqlite3.rs | 4 +- src/state.rs | 19 +- 46 files changed, 424 insertions(+), 293 deletions(-) create mode 100644 melib/src/utils/futures.rs create mode 100644 melib/src/utils/random.rs diff --git a/melib/src/addressbook.rs b/melib/src/addressbook.rs index 7817da32..427b8968 100644 --- a/melib/src/addressbook.rs +++ b/melib/src/addressbook.rs @@ -28,7 +28,7 @@ use std::{collections::HashMap, ops::Deref}; use uuid::Uuid; -use crate::{ +use crate::utils::{ datetime::{self, UnixTimestamp}, parsec::Parser, }; diff --git a/melib/src/addressbook/mutt.rs b/melib/src/addressbook/mutt.rs index 59cb4ff3..71dbb0ae 100644 --- a/melib/src/addressbook/mutt.rs +++ b/melib/src/addressbook/mutt.rs @@ -24,7 +24,7 @@ use std::collections::VecDeque; use super::*; -use crate::parsec::{is_not, map_res, match_literal_anycase, prefix, Parser}; +use crate::utils::parsec::{is_not, map_res, match_literal_anycase, prefix, Parser}; //alias [ ]
// From mutt doc: diff --git a/melib/src/addressbook/vcard.rs b/melib/src/addressbook/vcard.rs index 33de6324..fdfb0294 100644 --- a/melib/src/addressbook/vcard.rs +++ b/melib/src/addressbook/vcard.rs @@ -32,7 +32,7 @@ use std::{collections::HashMap, convert::TryInto}; use super::*; use crate::{ error::{Error, Result}, - parsec::{match_literal_anycase, one_or_more, peek, prefix, take_until, Parser}, + utils::parsec::{match_literal_anycase, one_or_more, peek, prefix, take_until, Parser}, }; /* Supported vcard versions */ @@ -222,8 +222,9 @@ impl TryInto for VCard { T102200Z T102200-0800 */ - card.birthday = crate::datetime::timestamp_from_string(val.value.as_str(), "%Y%m%d\0") - .unwrap_or_default(); + card.birthday = + crate::utils::datetime::timestamp_from_string(val.value.as_str(), "%Y%m%d\0") + .unwrap_or_default(); } if let Some(val) = self.0.remove("EMAIL") { card.set_email(val.value); diff --git a/melib/src/backends/imap.rs b/melib/src/backends/imap.rs index 493acf42..6f09e871 100644 --- a/melib/src/backends/imap.rs +++ b/melib/src/backends/imap.rs @@ -65,9 +65,9 @@ use crate::{ }, collection::Collection, conf::AccountSettings, - connections::timeout, email::{parser::BytesExt, *}, error::{Error, Result, ResultIntoError}, + utils::futures::timeout, }; pub type ImapNum = usize; diff --git a/melib/src/backends/imap/cache.rs b/melib/src/backends/imap/cache.rs index 37dc5307..36daf95a 100644 --- a/melib/src/backends/imap/cache.rs +++ b/melib/src/backends/imap/cache.rs @@ -106,17 +106,17 @@ pub use sqlite3_m::*; #[cfg(feature = "sqlite3")] mod sqlite3_m { use super::*; - use crate::sqlite3::{ + use crate::utils::sqlite3::{ self, rusqlite::types::{FromSql, FromSqlError, FromSqlResult, ToSql, ToSqlOutput}, - DatabaseDescription, + Connection, DatabaseDescription, }; type Sqlite3UID = i32; #[derive(Debug)] pub struct Sqlite3Cache { - connection: crate::sqlite3::Connection, + connection: Connection, loaded_mailboxes: BTreeSet, uid_store: Arc, } diff --git a/melib/src/backends/imap/connection.rs b/melib/src/backends/imap/connection.rs index dc37dfbf..2f0cdc77 100644 --- a/melib/src/backends/imap/connection.rs +++ b/melib/src/backends/imap/connection.rs @@ -21,10 +21,13 @@ use super::protocol_parser::{ImapLineSplit, ImapResponse, RequiredResponses, SelectResponse}; use crate::{ - backends::{MailboxHash, RefreshEvent}, - connections::{lookup_ipv4, timeout, Connection}, + backends::{BackendEvent, MailboxHash, RefreshEvent}, email::parser::BytesExt, error::*, + utils::{ + connections::{lookup_ipv4, Connection}, + futures::timeout, + }, LogLevel, }; extern crate native_tls; @@ -162,7 +165,7 @@ impl ImapStream { let stream = if server_conf.use_tls { (uid_store.event_consumer)( uid_store.account_hash, - crate::backends::BackendEvent::AccountStateChange { + BackendEvent::AccountStateChange { message: "Establishing TLS connection.".into(), }, ); @@ -172,9 +175,7 @@ impl ImapStream { } let connector = connector .build() - .chain_err_kind(crate::error::ErrorKind::Network( - crate::error::NetworkErrorKind::InvalidTLSConnection, - ))?; + .chain_err_kind(ErrorKind::Network(NetworkErrorKind::InvalidTLSConnection))?; let addr = lookup_ipv4(path, server_conf.server_port)?; @@ -263,8 +264,8 @@ impl ImapStream { midhandshake_stream = Some(stream); } p => { - p.chain_err_kind(crate::error::ErrorKind::Network( - crate::error::NetworkErrorKind::InvalidTLSConnection, + p.chain_err_kind(ErrorKind::Network( + NetworkErrorKind::InvalidTLSConnection, ))?; } } @@ -276,11 +277,7 @@ impl ImapStream { .chain_err_summary(|| format!("Could not initiate TLS negotiation to {}.", path))? } } else { - let addr = if let Ok(a) = lookup_ipv4(path, server_conf.server_port) { - a - } else { - return Err(Error::new(format!("Could not lookup address {}", &path))); - }; + let addr = lookup_ipv4(path, server_conf.server_port)?; AsyncWrapper::new(Connection::Tcp( if let Some(timeout) = server_conf.timeout { TcpStream::connect_timeout(&addr, timeout)? @@ -322,7 +319,7 @@ impl ImapStream { (uid_store.event_consumer)( uid_store.account_hash, - crate::backends::BackendEvent::AccountStateChange { + BackendEvent::AccountStateChange { message: "Negotiating server capabilities.".into(), }, ); @@ -344,7 +341,7 @@ impl ImapStream { &server_conf.server_hostname, String::from_utf8_lossy(&res) )) - .set_kind(ErrorKind::Bug)); + .set_kind(ErrorKind::ProtocolError)); } let capabilities = capabilities.unwrap(); @@ -355,7 +352,8 @@ impl ImapStream { return Err(Error::new(format!( "Could not connect to {}: server is not IMAP4rev1 compliant", &server_conf.server_hostname - ))); + )) + .set_kind(ErrorKind::ProtocolNotSupported)); } else if capabilities .iter() .any(|cap| cap.eq_ignore_ascii_case(b"LOGINDISABLED")) @@ -364,12 +362,12 @@ impl ImapStream { "Could not connect to {}: server does not accept logins [LOGINDISABLED]", &server_conf.server_hostname )) - .set_err_kind(crate::error::ErrorKind::Authentication)); + .set_err_kind(ErrorKind::Authentication)); } (uid_store.event_consumer)( uid_store.account_hash, - crate::backends::BackendEvent::AccountStateChange { + BackendEvent::AccountStateChange { message: "Attempting authentication.".into(), }, ); @@ -390,10 +388,14 @@ impl ImapStream { .map(|capability| String::from_utf8_lossy(capability).to_string()) .collect::>() .join(" ") - ))); + )) + .set_err_kind(ErrorKind::Authentication)); } let xoauth2 = base64::decode(&server_conf.server_password) - .map_err(|_| Error::new("Bad XOAUTH2 in config"))?; + .chain_err_summary(|| { + "Could not decode `server_password` from base64. Is the value correct?" + }) + .chain_err_kind(ErrorKind::Configuration)?; // TODO(#222): Improve this as soon as imap-codec supports XOAUTH2. ret.send_command(CommandBody::authenticate( AuthMechanism::Other( @@ -404,8 +406,10 @@ impl ImapStream { .await?; } _ => { - let username = AString::try_from(server_conf.server_username.as_str())?; - let password = AString::try_from(server_conf.server_password.as_str())?; + let username = AString::try_from(server_conf.server_username.as_str()) + .chain_err_kind(ErrorKind::Bug)?; + let password = AString::try_from(server_conf.server_password.as_str()) + .chain_err_kind(ErrorKind::Bug)?; ret.send_command(CommandBody::Login { username, @@ -435,7 +439,7 @@ impl ImapStream { "Could not connect. Server replied with '{}'", String::from_utf8_lossy(l[tag_start.len()..].trim()) )) - .set_err_kind(crate::error::ErrorKind::Authentication)); + .set_err_kind(ErrorKind::Authentication)); } should_break = true; } @@ -448,7 +452,6 @@ impl ImapStream { if capabilities.is_none() { /* sending CAPABILITY after LOGIN automatically is an RFC recommendation, so * check for lazy servers */ - drop(capabilities); ret.send_command(CommandBody::Capability).await?; ret.read_response(&mut res).await.unwrap(); let capabilities = protocol_parser::capabilities(&res)?.1; @@ -827,7 +830,7 @@ impl ImapConnection { ); (self.uid_store.event_consumer)( self.uid_store.account_hash, - crate::backends::BackendEvent::Notice { + BackendEvent::Notice { description: response_code.to_string(), content: None, level: LogLevel::ERROR, @@ -845,7 +848,7 @@ impl ImapConnection { ); (self.uid_store.event_consumer)( self.uid_store.account_hash, - crate::backends::BackendEvent::Notice { + BackendEvent::Notice { description: response_code.to_string(), content: None, level: LogLevel::ERROR, @@ -974,7 +977,7 @@ impl ImapConnection { "Trying to select a \\NoSelect mailbox: {}", &imap_path )) - .set_kind(crate::error::ErrorKind::Bug)); + .set_kind(ErrorKind::Bug)); } self.send_command(CommandBody::select(imap_path.as_str())?) .await?; @@ -1004,7 +1007,7 @@ impl ImapConnection { }) { (self.uid_store.event_consumer)( self.uid_store.account_hash, - crate::backends::BackendEvent::from(err), + BackendEvent::from(err), ); } } @@ -1061,7 +1064,7 @@ impl ImapConnection { "Trying to examine a \\NoSelect mailbox: {}", &imap_path )) - .set_kind(crate::error::ErrorKind::Bug)); + .set_kind(ErrorKind::Bug)); } self.send_command(CommandBody::examine(imap_path.as_str())?) .await?; @@ -1126,10 +1129,7 @@ impl ImapConnection { } pub fn add_refresh_event(&mut self, ev: RefreshEvent) { - (self.uid_store.event_consumer)( - self.uid_store.account_hash, - crate::backends::BackendEvent::Refresh(ev), - ); + (self.uid_store.event_consumer)(self.uid_store.account_hash, BackendEvent::Refresh(ev)); } async fn create_uid_msn_cache( diff --git a/melib/src/backends/imap/search.rs b/melib/src/backends/imap/search.rs index bdb8c709..69da83eb 100644 --- a/melib/src/backends/imap/search.rs +++ b/melib/src/backends/imap/search.rs @@ -24,8 +24,8 @@ use std::collections::VecDeque; use crate::{ - datetime::{formats::IMAP_DATE, timestamp_to_string}, search::*, + utils::datetime::{formats::IMAP_DATE, timestamp_to_string}, }; mod private { @@ -256,7 +256,7 @@ impl ToImapSearch for Query { #[cfg(test)] mod tests { use super::*; - use crate::parsec::Parser; + use crate::utils::parsec::Parser; #[test] fn test_imap_query_search() { diff --git a/melib/src/backends/jmap.rs b/melib/src/backends/jmap.rs index 54efcabe..38af6beb 100644 --- a/melib/src/backends/jmap.rs +++ b/melib/src/backends/jmap.rs @@ -34,9 +34,9 @@ use serde_json::Value; use crate::{ backends::*, conf::AccountSettings, - connections::timeout, email::*, error::{Error, Result}, + utils::futures::{sleep, timeout}, Collection, }; @@ -344,7 +344,7 @@ impl MailBackend for JmapType { conn.email_changes(mailbox_hash).await?; } } - crate::connections::sleep(Duration::from_secs(60)).await; + sleep(Duration::from_secs(60)).await; } })) } diff --git a/melib/src/backends/jmap/objects/email.rs b/melib/src/backends/jmap/objects/email.rs index e5f78985..2e45d99c 100644 --- a/melib/src/backends/jmap/objects/email.rs +++ b/melib/src/backends/jmap/objects/email.rs @@ -29,6 +29,7 @@ use super::*; use crate::{ backends::jmap::rfc8620::bool_false, email::address::{Address, MailboxAddress}, + utils::datetime, }; mod import; @@ -265,8 +266,7 @@ impl std::convert::From for crate::Envelope { env.set_datetime(d); } if let Some(ref mut sent_at) = t.sent_at { - let unix = - crate::datetime::rfc3339_to_timestamp(sent_at.as_bytes().to_vec()).unwrap_or(0); + let unix = datetime::rfc3339_to_timestamp(sent_at.as_bytes().to_vec()).unwrap_or(0); env.set_datetime(unix); env.set_date(std::mem::replace(sent_at, String::new()).as_bytes()); } @@ -588,10 +588,10 @@ impl From for Filter { fn from(val: crate::search::Query) -> Self { let mut ret = Filter::Condition(EmailFilterCondition::new().into()); fn rec(q: &crate::search::Query, f: &mut Filter) { - use crate::{ - datetime::{formats::RFC3339_DATE, timestamp_to_string}, - search::Query::*, - }; + use datetime::{formats::RFC3339_DATE, timestamp_to_string}; + + use crate::search::Query::*; + match q { Subject(t) => { *f = Filter::Condition(EmailFilterCondition::new().subject(t.clone()).into()); diff --git a/melib/src/backends/maildir.rs b/melib/src/backends/maildir.rs index 97b4aaf7..6a2202f4 100644 --- a/melib/src/backends/maildir.rs +++ b/melib/src/backends/maildir.rs @@ -40,7 +40,7 @@ use crate::{ backends::*, email::Flag, error::{Error, Result}, - shellexpand::ShellExpandTrait, + utils::shellexpand::ShellExpandTrait, }; /// `BackendOp` implementor for Maildir diff --git a/melib/src/backends/maildir/backend.rs b/melib/src/backends/maildir/backend.rs index 8ee869e7..0dad802b 100644 --- a/melib/src/backends/maildir/backend.rs +++ b/melib/src/backends/maildir/backend.rs @@ -32,7 +32,7 @@ use crate::{ conf::AccountSettings, email::{Envelope, EnvelopeHash, Flag}, error::{Error, ErrorKind, Result}, - shellexpand::ShellExpandTrait, + utils::shellexpand::ShellExpandTrait, Collection, }; diff --git a/melib/src/backends/mbox.rs b/melib/src/backends/mbox.rs index 0edbfb78..5fb25eab 100644 --- a/melib/src/backends/mbox.rs +++ b/melib/src/backends/mbox.rs @@ -146,7 +146,7 @@ use crate::{ email::{parser::BytesExt, *}, error::{Error, ErrorKind, Result}, get_path_hash, - shellexpand::ShellExpandTrait, + utils::shellexpand::ShellExpandTrait, }; extern crate notify; diff --git a/melib/src/backends/mbox/write.rs b/melib/src/backends/mbox/write.rs index a9b41a2b..47fd8df9 100644 --- a/melib/src/backends/mbox/write.rs +++ b/melib/src/backends/mbox/write.rs @@ -20,7 +20,7 @@ */ use super::*; -use crate::datetime; +use crate::utils::datetime; impl MboxFormat { pub fn append( diff --git a/melib/src/backends/nntp.rs b/melib/src/backends/nntp.rs index 7f14b60e..197a388e 100644 --- a/melib/src/backends/nntp.rs +++ b/melib/src/backends/nntp.rs @@ -52,9 +52,9 @@ use futures::{lock::Mutex as FutureMutex, stream::Stream}; use crate::{ backends::*, conf::AccountSettings, - connections::timeout, email::*, error::{Error, Result, ResultIntoError}, + utils::futures::timeout, Collection, }; pub type UID = usize; @@ -286,8 +286,11 @@ impl MailBackend for NntpType { let mut conn = timeout(Some(Duration::from_secs(60 * 16)), connection.lock()).await?; if let Some(mut latest_article) = latest_article { let timestamp = latest_article - 10 * 60; - let datetime_str = - crate::datetime::timestamp_to_string(timestamp, Some("%Y%m%d %H%M%S"), true); + let datetime_str = crate::utils::datetime::timestamp_to_string( + timestamp, + Some("%Y%m%d %H%M%S"), + true, + ); if newnews_support { conn.send_command( diff --git a/melib/src/backends/nntp/connection.rs b/melib/src/backends/nntp/connection.rs index f823969e..f9dd2a79 100644 --- a/melib/src/backends/nntp/connection.rs +++ b/melib/src/backends/nntp/connection.rs @@ -21,10 +21,10 @@ use crate::{ backends::{BackendMailbox, MailboxHash}, - connections::{lookup_ipv4, Connection}, email::parser::BytesExt, error::*, log, + utils::connections::{lookup_ipv4, Connection}, }; extern crate native_tls; use std::{collections::HashSet, future::Future, pin::Pin, sync::Arc, time::Instant}; diff --git a/melib/src/backends/notmuch.rs b/melib/src/backends/notmuch.rs index 17a70477..9a63f207 100644 --- a/melib/src/backends/notmuch.rs +++ b/melib/src/backends/notmuch.rs @@ -35,7 +35,7 @@ use crate::{ conf::AccountSettings, email::{Envelope, EnvelopeHash, Flag}, error::{Error, Result}, - shellexpand::ShellExpandTrait, + utils::shellexpand::ShellExpandTrait, Collection, }; diff --git a/melib/src/backends/notmuch/message.rs b/melib/src/backends/notmuch/message.rs index e03c61af..c9be3d6c 100644 --- a/melib/src/backends/notmuch/message.rs +++ b/melib/src/backends/notmuch/message.rs @@ -81,7 +81,7 @@ impl<'m> Message<'m> { unsafe { CStr::from_ptr(msg_id) } } - pub fn date(&self) -> crate::datetime::UnixTimestamp { + pub fn date(&self) -> crate::UnixTimestamp { (unsafe { call!(self.lib, notmuch_message_get_date)(self.message) }) as u64 } diff --git a/melib/src/backends/notmuch/thread.rs b/melib/src/backends/notmuch/thread.rs index ea2b47e5..97f9433a 100644 --- a/melib/src/backends/notmuch/thread.rs +++ b/melib/src/backends/notmuch/thread.rs @@ -34,7 +34,7 @@ impl<'q> Thread<'q> { ThreadHash::from(c_str.to_bytes()) } - pub fn date(&self) -> crate::datetime::UnixTimestamp { + pub fn date(&self) -> crate::UnixTimestamp { (unsafe { call!(self.lib, notmuch_thread_get_newest_date)(self.ptr) }) as u64 } diff --git a/melib/src/email.rs b/melib/src/email.rs index 5ec7533a..f18cf691 100644 --- a/melib/src/email.rs +++ b/melib/src/email.rs @@ -122,11 +122,10 @@ use imap_codec::{ use smallvec::SmallVec; use crate::{ - datetime::UnixTimestamp, error::{Error, Result}, parser::BytesExt, thread::ThreadNodeHash, - TagHash, + TagHash, UnixTimestamp, }; #[cfg(feature = "imap_backend")] diff --git a/melib/src/email/compose.rs b/melib/src/email/compose.rs index 4e533752..607c9da3 100644 --- a/melib/src/email/compose.rs +++ b/melib/src/email/compose.rs @@ -33,12 +33,11 @@ use xdg_utils::query_mime_info; use super::*; use crate::{ - datetime, email::{ attachment_types::{Charset, ContentTransferEncoding, ContentType, MultipartType}, attachments::AttachmentBuilder, }, - shellexpand::ShellExpandTrait, + utils::{datetime, shellexpand::ShellExpandTrait}, }; pub mod mime; diff --git a/melib/src/email/compose/random.rs b/melib/src/email/compose/random.rs index 67fba6ca..aef1712d 100644 --- a/melib/src/email/compose/random.rs +++ b/melib/src/email/compose/random.rs @@ -19,31 +19,7 @@ * along with meli. If not, see . */ -use std::{char, fs::File, io::prelude::*, time::SystemTime}; - -fn random_u64() -> u64 { - let mut f = File::open("/dev/urandom").unwrap(); - let mut buffer = [0; 8]; - - // read exactly 10 bytes - f.read_exact(&mut buffer).unwrap(); - - u64::from(buffer[0]) - | (u64::from(buffer[1]) << 8) - | (u64::from(buffer[2]) << 16) - | (u64::from(buffer[3]) << 24) - | (u64::from(buffer[4]) << 32) - | (u64::from(buffer[5]) << 40) - | (u64::from(buffer[6]) << 48) - | (u64::from(buffer[7]) << 56) -} - -fn clock() -> u64 { - SystemTime::now() - .duration_since(SystemTime::UNIX_EPOCH) - .unwrap() - .as_secs() -} +use crate::utils::random::{clock, random_u64}; fn base36(mut m: u64) -> String { let mut stack = Vec::with_capacity(32); diff --git a/melib/src/email/mailto.rs b/melib/src/email/mailto.rs index 7c393982..b4a801a5 100644 --- a/melib/src/email/mailto.rs +++ b/melib/src/email/mailto.rs @@ -29,7 +29,7 @@ use std::convert::TryFrom; use super::*; use crate::{ email::headers::HeaderMap, - percent_encoding::{AsciiSet, CONTROLS}, + utils::percent_encoding::{AsciiSet, CONTROLS}, }; #[derive(Debug, Clone, Eq, PartialEq)] diff --git a/melib/src/email/parser.rs b/melib/src/email/parser.rs index 2d68350b..8a57aa21 100644 --- a/melib/src/email/parser.rs +++ b/melib/src/email/parser.rs @@ -42,8 +42,7 @@ use crate::{ mailto::Mailto, }, error::{Error, Result, ResultIntoError}, - html_escape::HtmlEntity, - percent_encoding::percent_decode, + utils::{html_escape::HtmlEntity, percent_encoding::percent_decode}, }; macro_rules! to_str { @@ -330,7 +329,7 @@ pub fn mail(input: &[u8]) -> Result<(Vec<(&[u8], &[u8])>, &[u8])> { pub mod dates { /*! Date values in headers */ use super::{generic::*, *}; - use crate::datetime::UnixTimestamp; + use crate::utils::datetime::UnixTimestamp; fn take_n_digits(n: usize) -> impl Fn(&[u8]) -> IResult<&[u8], &[u8]> { move |input: &[u8]| { @@ -451,7 +450,7 @@ pub mod dates { accum.extend_from_slice(b" "); accum.extend_from_slice(sign); accum.extend_from_slice(zone); - match crate::datetime::rfc822_to_timestamp(accum.to_vec()) { + match crate::utils::datetime::rfc822_to_timestamp(accum.to_vec()) { Ok(t) => Ok((input, t)), Err(_err) => Err(nom::Err::Error( ( @@ -513,7 +512,7 @@ pub mod dates { accum.extend_from_slice(sign); accum.extend_from_slice(zone); } - match crate::datetime::rfc822_to_timestamp(accum.to_vec()) { + match crate::utils::datetime::rfc822_to_timestamp(accum.to_vec()) { Ok(t) => Ok((input, t)), Err(_err) => Err(nom::Err::Error( ( @@ -582,7 +581,7 @@ pub mod dates { Ok((input, ret)) } - pub fn rfc5322_date(input: &[u8]) -> Result { + pub fn rfc5322_date(input: &[u8]) -> Result { date_time(input) .or_else(|_| { //let (_, mut parsed_result) = encodings::phrase(&eat_comments(input), false)?; @@ -616,7 +615,7 @@ pub mod dates { parsed_result[pos] = b'+'; } - crate::datetime::rfc822_to_timestamp(parsed_result.trim()) + crate::utils::datetime::rfc822_to_timestamp(parsed_result.trim()) */ } diff --git a/melib/src/error.rs b/melib/src/error.rs index d5d9261b..1c4e6080 100644 --- a/melib/src/error.rs +++ b/melib/src/error.rs @@ -183,7 +183,7 @@ pub enum NetworkErrorKind { } impl NetworkErrorKind { - pub fn as_str(&self) -> &'static str { + pub const fn as_str(&self) -> &'static str { use NetworkErrorKind::*; match self { None => "Network", @@ -242,6 +242,19 @@ impl NetworkErrorKind { NetworkAuthenticationRequired => "Network Authentication Required", } } + + /// Error kind means network is certainly down. + pub const fn is_network_down(&self) -> bool { + use NetworkErrorKind::*; + matches!( + self, + BadGateway + | ServiceUnavailable + | GatewayTimeout + | NetworkAuthenticationRequired + | ConnectionFailed + ) + } } impl Default for NetworkErrorKind { @@ -312,6 +325,13 @@ pub enum ErrorKind { External, Authentication, Configuration, + /// Protocol error. + /// + /// `EPROTO 71 Protocol error` + ProtocolError, + /// Protocol is not supported. + /// It could be the wrong type or version. + ProtocolNotSupported, Bug, Network(NetworkErrorKind), Timeout, @@ -332,6 +352,9 @@ impl fmt::Display for ErrorKind { Self::Authentication => "Authentication", Self::Bug => "Bug, please report this!", Self::Network(ref inner) => inner.as_str(), + Self::ProtocolError => "Protocol error", + Self::ProtocolNotSupported => + "Protocol is not supported. It could be the wrong type or version.", Self::Timeout => "Timeout", Self::OSError => "OS Error", Self::Configuration => "Configuration", @@ -343,18 +366,29 @@ impl fmt::Display for ErrorKind { } } +macro_rules! is_variant { + ($n:ident, $($var:tt)+) => { + #[inline] + pub fn $n(&self) -> bool { + matches!(self, Self::$($var)*) + } + }; +} + impl ErrorKind { - pub fn is_network(&self) -> bool { - matches!(self, Self::Network(_)) - } - - pub fn is_timeout(&self) -> bool { - matches!(self, Self::Timeout) - } - - pub fn is_authentication(&self) -> bool { - matches!(self, Self::Authentication) - } + is_variant! { is_authentication, Authentication } + is_variant! { is_bug, Bug } + is_variant! { is_configuration, Configuration } + is_variant! { is_external, External } + is_variant! { is_network, Network(_) } + is_variant! { is_network_down, Network(ref k) if k.is_network_down() } + is_variant! { is_not_implemented, NotImplemented } + is_variant! { is_not_supported, NotSupported } + is_variant! { is_oserror, OSError } + is_variant! { is_protocol_error, ProtocolError } + is_variant! { is_protocol_not_supported, ProtocolNotSupported } + is_variant! { is_timeout, Timeout } + is_variant! { is_value_error, ValueError } } #[derive(Debug, Clone)] @@ -709,3 +743,12 @@ impl<'a> From<&'a Error> for Error { kind.clone() } } + +impl From for Error { + #[inline] + fn from(kind: base64::DecodeError) -> Error { + Error::new("base64 decoding failed") + .set_source(Some(Arc::new(kind))) + .set_kind(ErrorKind::ValueError) + } +} diff --git a/melib/src/lib.rs b/melib/src/lib.rs index e561a04c..a732e7e4 100644 --- a/melib/src/lib.rs +++ b/melib/src/lib.rs @@ -82,9 +82,10 @@ pub mod dbg { #[cfg(feature = "unicode_algorithms")] pub mod text_processing; -pub use utils::{datetime::UnixTimestamp, *}; - -pub use self::logging::{LogLevel, StderrLogger}; +pub use utils::{ + datetime::UnixTimestamp, + logging::{LogLevel, StderrLogger}, +}; pub mod addressbook; pub use addressbook::*; @@ -98,13 +99,13 @@ pub use conf::*; pub mod email; pub use email::*; pub mod error; -pub use crate::error::*; +pub use error::*; pub mod thread; pub use thread::*; pub mod search; #[macro_use] -mod utils; +pub mod utils; #[cfg(feature = "gpgme")] pub mod gpgme; @@ -158,4 +159,4 @@ impl core::fmt::Display for Bytes { } } -pub use shellexpand::ShellExpandTrait; +pub use utils::shellexpand::ShellExpandTrait; diff --git a/melib/src/search.rs b/melib/src/search.rs index cedcca76..9df959ea 100644 --- a/melib/src/search.rs +++ b/melib/src/search.rs @@ -24,7 +24,7 @@ use std::{borrow::Cow, convert::TryFrom}; pub use query_parser::query; use Query::*; -use crate::{ +use crate::utils::{ datetime::{formats, UnixTimestamp}, parsec::*, }; @@ -152,9 +152,10 @@ pub mod query_parser { fn date<'a>() -> impl Parser<'a, UnixTimestamp> { move |input| { literal().parse(input).and_then(|(next_input, result)| { - if let Ok((_, t)) = - crate::datetime::parse_timestamp_from_string(result, formats::RFC3339_DATE) - { + if let Ok((_, t)) = crate::utils::datetime::parse_timestamp_from_string( + result, + formats::RFC3339_DATE, + ) { Ok((next_input, t)) } else { Err(next_input) @@ -368,8 +369,8 @@ pub mod query_parser { /// # Invocation /// ``` /// use melib::{ - /// parsec::Parser, /// search::{query, Query}, + /// utils::parsec::Parser, /// }; /// /// let input = "test"; diff --git a/melib/src/sieve.rs b/melib/src/sieve.rs index 6a66a264..1e34cf71 100644 --- a/melib/src/sieve.rs +++ b/melib/src/sieve.rs @@ -19,7 +19,7 @@ * along with meli. If not, see . */ -use crate::parsec::*; +use crate::utils::parsec::*; #[derive(Debug, Clone, PartialEq, Eq)] pub struct RuleBlock(pub Vec); @@ -683,7 +683,7 @@ mod test { parser::*, ActionCommand::*, AddressOperator::*, CharacterOperator::*, ConditionRule::*, ControlCommand::*, IntegerOperator::*, MatchOperator::*, Rule::*, RuleBlock, }; - use crate::parsec::Parser; + use crate::utils::parsec::Parser; #[test] fn test_sieve_parse_strings() { diff --git a/melib/src/smtp.rs b/melib/src/smtp.rs index 5cdb83b4..a11982c2 100644 --- a/melib/src/smtp.rs +++ b/melib/src/smtp.rs @@ -80,9 +80,9 @@ use smallvec::SmallVec; use smol::{unblock, Async as AsyncWrapper}; use crate::{ - connections::{lookup_ipv4, Connection}, email::{parser::BytesExt, Address, Envelope}, error::{Error, Result, ResultIntoError}, + utils::connections::{lookup_ipv4, Connection}, }; /// Kind of server security (StartTLS/TLS/None) the client should attempt diff --git a/melib/src/thread.rs b/melib/src/thread.rs index b8ca6950..769e635e 100644 --- a/melib/src/thread.rs +++ b/melib/src/thread.rs @@ -34,8 +34,8 @@ */ use crate::{ - datetime::UnixTimestamp, email::{address::StrBuild, parser::BytesExt, *}, + UnixTimestamp, }; mod iterators; @@ -1674,7 +1674,7 @@ fn save_graph( let mut file = File::create(format!( "/tmp/meli/threads/threads_{}.json", - crate::datetime::now() + crate::utils::datetime::now() )) .unwrap(); file.write_all(s.as_bytes()).unwrap(); diff --git a/melib/src/utils/connections.rs b/melib/src/utils/connections.rs index f7674a35..357b748a 100644 --- a/melib/src/utils/connections.rs +++ b/melib/src/utils/connections.rs @@ -275,24 +275,3 @@ pub fn lookup_ipv4(host: &str, port: u16) -> crate::Result ), ) } - -use futures::future::{self, Either, Future}; - -pub async fn timeout(dur: Option, f: impl Future) -> crate::Result { - futures::pin_mut!(f); - if let Some(dur) = dur { - match future::select(f, smol::Timer::after(dur)).await { - Either::Left((out, _)) => Ok(out), - Either::Right(_) => { - Err(crate::error::Error::new("Timed out.") - .set_kind(crate::error::ErrorKind::Timeout)) - } - } - } else { - Ok(f.await) - } -} - -pub async fn sleep(dur: Duration) { - smol::Timer::after(dur).await; -} diff --git a/melib/src/utils/futures.rs b/melib/src/utils/futures.rs new file mode 100644 index 00000000..1c100c58 --- /dev/null +++ b/melib/src/utils/futures.rs @@ -0,0 +1,43 @@ +/* + * meli - melib library + * + * Copyright 2020 Manos Pitsidianakis + * + * This file is part of meli. + * + * meli is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * meli is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with meli. If not, see . + */ + +use std::time::Duration; + +use futures::future::{self, Either, Future}; + +pub async fn timeout(dur: Option, f: impl Future) -> crate::Result { + futures::pin_mut!(f); + if let Some(dur) = dur { + match future::select(f, smol::Timer::after(dur)).await { + Either::Left((out, _)) => Ok(out), + Either::Right(_) => { + Err(crate::error::Error::new("Timed out.") + .set_kind(crate::error::ErrorKind::Timeout)) + } + } + } else { + Ok(f.await) + } +} + +pub async fn sleep(dur: Duration) { + smol::Timer::after(dur).await; +} diff --git a/melib/src/utils/logging.rs b/melib/src/utils/logging.rs index 0e991d71..3b0478ad 100644 --- a/melib/src/utils/logging.rs +++ b/melib/src/utils/logging.rs @@ -220,7 +220,7 @@ impl StderrLogger { #[cfg(not(test))] pub fn change_log_dest(&mut self, path: PathBuf) { - use crate::shellexpand::ShellExpandTrait; + use crate::utils::shellexpand::ShellExpandTrait; let path = path.expand(); // expand shell stuff let mut dest = self.dest.lock().unwrap(); @@ -254,7 +254,7 @@ impl Log for StderrLogger { ) -> Option<()> { writer .write_all( - crate::datetime::timestamp_to_string(crate::datetime::now(), None, false) + super::datetime::timestamp_to_string(super::datetime::now(), None, false) .as_bytes(), ) .ok()?; diff --git a/melib/src/utils/mod.rs b/melib/src/utils/mod.rs index 288e74d8..e47abd97 100644 --- a/melib/src/utils/mod.rs +++ b/melib/src/utils/mod.rs @@ -23,6 +23,8 @@ pub mod connections; pub mod datetime; +pub mod futures; +pub mod random; #[macro_use] pub mod logging; pub mod parsec; diff --git a/melib/src/utils/parsec.rs b/melib/src/utils/parsec.rs index 1ddc7592..6c16d23b 100644 --- a/melib/src/utils/parsec.rs +++ b/melib/src/utils/parsec.rs @@ -23,7 +23,7 @@ use std::borrow::Cow; -use crate::datetime::{parse_timestamp_from_string, UnixTimestamp}; +use crate::utils::datetime::{parse_timestamp_from_string, UnixTimestamp}; pub type Result<'a, Output> = std::result::Result<(&'a str, Output), &'a str>; @@ -445,8 +445,8 @@ pub fn is_not<'a>(slice: &'static [u8]) -> impl Parser<'a, &'a str> { /// Try alternative parsers in order until one succeeds. /// /// ```rust -/// # use melib::parsec::{Parser, quoted_slice, match_literal, alt, delimited, prefix}; -/// +/// # use melib::utils::parsec::{Parser, quoted_slice, match_literal, alt, delimited, prefix}; +/// # /// let parser = |input| { /// alt([ /// delimited(match_literal("{"), quoted_slice(), match_literal("}")), @@ -456,9 +456,8 @@ pub fn is_not<'a>(slice: &'static [u8]) -> impl Parser<'a, &'a str> { /// }; /// /// let input1: &str = "{\"quoted\"}"; -/// let input2: &str = "[\"quoted\"]"; /// assert_eq!(Ok(("", "quoted")), parser.parse(input1)); -/// +/// let input2: &str = "[\"quoted\"]"; /// assert_eq!(Ok(("", "quoted")), parser.parse(input2)); /// ``` pub fn alt<'a, P, A, const N: usize>(parsers: [P; N]) -> impl Parser<'a, A> @@ -584,7 +583,7 @@ pub fn take<'a>(count: usize) -> impl Parser<'a, &'a str> { /// ///```rust /// # use std::str::FromStr; -/// # use melib::parsec::{Parser, delimited, match_literal, map_res, is_a, take_literal}; +/// # use melib::utils::parsec::{Parser, delimited, match_literal, map_res, is_a, take_literal}; /// let lit: &str = "{31}\r\nThere is no script by that name\r\n"; /// assert_eq!( /// take_literal(delimited( diff --git a/melib/src/utils/random.rs b/melib/src/utils/random.rs new file mode 100644 index 00000000..0e5f49de --- /dev/null +++ b/melib/src/utils/random.rs @@ -0,0 +1,71 @@ +/* + * meli - melib crate. + * + * Copyright 2017-2020 Manos Pitsidianakis + * + * This file is part of meli. + * + * meli is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * meli is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with meli. If not, see . + */ + +use std::{fs::File, io::prelude::*, time::SystemTime}; + +const EXPECT: &str = "Could not open/read /dev/urandom"; + +pub fn random_u64() -> u64 { + let mut f = File::open("/dev/urandom").expect(EXPECT); + let mut buffer = [0; 8]; + + // read exactly 8 bytes + f.read_exact(&mut buffer).expect(EXPECT); + + u64::from(buffer[0]) + | (u64::from(buffer[1]) << 8) + | (u64::from(buffer[2]) << 16) + | (u64::from(buffer[3]) << 24) + | (u64::from(buffer[4]) << 32) + | (u64::from(buffer[5]) << 40) + | (u64::from(buffer[6]) << 48) + | (u64::from(buffer[7]) << 56) +} + +pub fn random_u32() -> u32 { + let mut f = File::open("/dev/urandom").expect(EXPECT); + let mut buffer = [0; 4]; + + // read exactly 4 bytes + f.read_exact(&mut buffer).expect(EXPECT); + + u32::from(buffer[0]) + | (u32::from(buffer[1]) << 8) + | (u32::from(buffer[2]) << 16) + | (u32::from(buffer[3]) << 24) +} + +pub fn random_u8() -> u8 { + let mut f = File::open("/dev/urandom").expect(EXPECT); + let mut buffer = [0; 1]; + + // read exactly 1 byte + f.read_exact(&mut buffer).expect(EXPECT); + + buffer[0] +} + +pub fn clock() -> u64 { + SystemTime::now() + .duration_since(SystemTime::UNIX_EPOCH) + .unwrap() + .as_secs() +} diff --git a/src/command.rs b/src/command.rs index 22b24907..ea58c3f6 100644 --- a/src/command.rs +++ b/src/command.rs @@ -1005,107 +1005,6 @@ pub fn parse_command(input: &[u8]) -> Result { .map_err(|err| err.into()) } -#[test] -fn test_parser() { - let mut input = "sort".to_string(); - macro_rules! match_input { - ($input:expr) => {{ - let mut sugg = Default::default(); - let mut vec = vec![]; - //print!("{}", $input); - for (_tags, _desc, tokens) in COMMAND_COMPLETION.iter() { - //println!("{:?}, {:?}, {:?}", _tags, _desc, tokens); - let m = tokens.matches(&mut $input.as_str(), &mut sugg); - if !m.is_empty() { - vec.push(tokens); - //print!("{:?} ", desc); - //println!(" result = {:#?}\n\n", m); - } - } - //println!("suggestions = {:#?}", sugg); - sugg.into_iter() - .map(|s| format!("{}{}", $input.as_str(), s.as_str())) - .collect::>() - }}; - } - assert_eq!( - &match_input!(input), - &IntoIterator::into_iter(["sort date".to_string(), "sort subject".to_string()]).collect(), - ); - input = "so".to_string(); - assert_eq!( - &match_input!(input), - &IntoIterator::into_iter(["sort".to_string()]).collect(), - ); - input = "so ".to_string(); - assert_eq!(&match_input!(input), &HashSet::default(),); - input = "to".to_string(); - assert_eq!( - &match_input!(input), - &IntoIterator::into_iter(["toggle".to_string()]).collect(), - ); - input = "toggle ".to_string(); - assert_eq!( - &match_input!(input), - &IntoIterator::into_iter([ - "toggle mouse".to_string(), - "toggle sign".to_string(), - "toggle encrypt".to_string(), - "toggle thread_snooze".to_string() - ]) - .collect(), - ); -} - -#[test] -#[ignore] -fn test_parser_interactive() { - use std::io; - let mut input = String::new(); - loop { - input.clear(); - print!("> "); - match io::stdin().read_line(&mut input) { - Ok(_n) => { - println!("Input is {:?}", input.as_str().trim()); - let mut sugg = Default::default(); - let mut vec = vec![]; - //print!("{}", input); - for (_tags, _desc, tokens) in COMMAND_COMPLETION.iter() { - //println!("{:?}, {:?}, {:?}", _tags, _desc, tokens); - let m = tokens.matches(&mut input.as_str().trim(), &mut sugg); - if !m.is_empty() { - vec.push(tokens); - //print!("{:?} ", desc); - //println!(" result = {:#?}\n\n", m); - } - } - println!( - "suggestions = {:#?}", - sugg.into_iter() - .zip(vec.into_iter()) - .map(|(s, v)| format!( - "{}{} {:?}", - input.as_str().trim(), - if input.trim().is_empty() { - s.trim() - } else { - s.as_str() - }, - v - )) - .collect::>() - ); - if input.trim() == "quit" { - break; - } - } - Err(error) => println!("error: {}", error), - } - } - println!("alright"); -} - /// Get command suggestions for input pub fn command_completion_suggestions(input: &str) -> Vec { use crate::melib::ShellExpandTrait; @@ -1124,3 +1023,110 @@ pub fn command_completion_suggestions(input: &str) -> Vec { .map(|s| format!("{}{}", input, s.as_str())) .collect::>() } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_command_parser() { + let mut input = "sort".to_string(); + macro_rules! match_input { + ($input:expr) => {{ + let mut sugg = Default::default(); + let mut vec = vec![]; + //print!("{}", $input); + for (_tags, _desc, tokens) in COMMAND_COMPLETION.iter() { + //println!("{:?}, {:?}, {:?}", _tags, _desc, tokens); + let m = tokens.matches(&mut $input.as_str(), &mut sugg); + if !m.is_empty() { + vec.push(tokens); + //print!("{:?} ", desc); + //println!(" result = {:#?}\n\n", m); + } + } + //println!("suggestions = {:#?}", sugg); + sugg.into_iter() + .map(|s| format!("{}{}", $input.as_str(), s.as_str())) + .collect::>() + }}; + } + assert_eq!( + &match_input!(input), + &IntoIterator::into_iter(["sort date".to_string(), "sort subject".to_string()]) + .collect(), + ); + input = "so".to_string(); + assert_eq!( + &match_input!(input), + &IntoIterator::into_iter(["sort".to_string()]).collect(), + ); + input = "so ".to_string(); + assert_eq!(&match_input!(input), &HashSet::default(),); + input = "to".to_string(); + assert_eq!( + &match_input!(input), + &IntoIterator::into_iter(["toggle".to_string()]).collect(), + ); + input = "toggle ".to_string(); + assert_eq!( + &match_input!(input), + &IntoIterator::into_iter([ + "toggle mouse".to_string(), + "toggle sign".to_string(), + "toggle encrypt".to_string(), + "toggle thread_snooze".to_string() + ]) + .collect(), + ); + } + + #[test] + #[ignore] + fn test_parser_interactive() { + use std::io; + let mut input = String::new(); + loop { + input.clear(); + print!("> "); + match io::stdin().read_line(&mut input) { + Ok(_n) => { + println!("Input is {:?}", input.as_str().trim()); + let mut sugg = Default::default(); + let mut vec = vec![]; + //print!("{}", input); + for (_tags, _desc, tokens) in COMMAND_COMPLETION.iter() { + //println!("{:?}, {:?}, {:?}", _tags, _desc, tokens); + let m = tokens.matches(&mut input.as_str().trim(), &mut sugg); + if !m.is_empty() { + vec.push(tokens); + //print!("{:?} ", desc); + //println!(" result = {:#?}\n\n", m); + } + } + println!( + "suggestions = {:#?}", + sugg.into_iter() + .zip(vec.into_iter()) + .map(|(s, v)| format!( + "{}{} {:?}", + input.as_str().trim(), + if input.trim().is_empty() { + s.trim() + } else { + s.as_str() + }, + v + )) + .collect::>() + ); + if input.trim() == "quit" { + break; + } + } + Err(error) => println!("error: {}", error), + } + } + println!("alright"); + } +} diff --git a/src/components/mail/compose.rs b/src/components/mail/compose.rs index 25640e31..4b17636a 100644 --- a/src/components/mail/compose.rs +++ b/src/components/mail/compose.rs @@ -2448,7 +2448,7 @@ fn attribution_string( .map(|addr| addr.get_email()) .unwrap_or_else(|| "\"\"".to_string()), ); - melib::datetime::timestamp_to_string(date, Some(fmt.as_str()), posix) + melib::utils::datetime::timestamp_to_string(date, Some(fmt.as_str()), posix) } #[cfg(test)] diff --git a/src/components/mail/compose/hooks.rs b/src/components/mail/compose/hooks.rs index 166adeec..cc5dffc3 100644 --- a/src/components/mail/compose/hooks.rs +++ b/src/components/mail/compose/hooks.rs @@ -22,6 +22,8 @@ //! Pre-submission hooks for draft validation and/or transformations. pub use std::borrow::Cow; +use melib::email::headers::HeaderName; + use super::*; pub enum HookFn { @@ -153,10 +155,10 @@ impl std::ops::DerefMut for Hook { } fn past_date_warn(_ctx: &mut Context, draft: &mut Draft) -> Result<()> { - use melib::datetime::*; + use melib::utils::datetime::*; if let Some(v) = draft .headers - .get("Date") + .get(HeaderName::DATE) .map(rfc822_to_timestamp) .and_then(Result::ok) { @@ -181,8 +183,8 @@ pub const PASTDATEWARN: Hook = Hook { }; fn important_header_warn(_ctx: &mut Context, draft: &mut Draft) -> Result<()> { - for hdr in ["From", "To"] { - match draft.headers.get(hdr).map(melib::Address::list_try_from) { + for hdr in [HeaderName::FROM, HeaderName::TO] { + match draft.headers.get(&hdr).map(melib::Address::list_try_from) { Some(Ok(_)) => {} Some(Err(err)) => return Err(format!("{hdr} header value is invalid ({err}).").into()), None => return Err(format!("{hdr} header is missing and should be present.").into()), @@ -192,8 +194,8 @@ fn important_header_warn(_ctx: &mut Context, draft: &mut Draft) -> Result<()> { { match draft .headers - .get("Date") - .map(melib::datetime::rfc822_to_timestamp) + .get(HeaderName::DATE) + .map(melib::utils::datetime::rfc822_to_timestamp) { Some(Err(err)) => return Err(format!("Date header value is invalid ({err}).").into()), Some(Ok(0)) => return Err("Date header value is invalid.".into()), @@ -201,10 +203,10 @@ fn important_header_warn(_ctx: &mut Context, draft: &mut Draft) -> Result<()> { } } - for hdr in ["Cc", "Bcc"] { + for hdr in [HeaderName::CC, HeaderName::BCC] { if let Some(Err(err)) = draft .headers - .get(hdr) + .get(&hdr) .filter(|v| !v.trim().is_empty()) .map(melib::Address::list_try_from) { @@ -223,7 +225,7 @@ pub const HEADERWARN: Hook = Hook { fn missing_attachment_warn(_ctx: &mut Context, draft: &mut Draft) -> Result<()> { if draft .headers - .get("Subject") + .get(HeaderName::SUBJECT) .map(|s| s.to_lowercase().contains("attach")) .unwrap_or(false) && draft.attachments.is_empty() @@ -247,7 +249,7 @@ pub const MISSINGATTACHMENTWARN: Hook = Hook { fn empty_draft_warn(_ctx: &mut Context, draft: &mut Draft) -> Result<()> { if draft .headers - .get("Subject") + .get(HeaderName::SUBJECT) .filter(|v| !v.trim().is_empty()) .is_none() && draft.body.trim().is_empty() diff --git a/src/components/mail/listing/conversations.rs b/src/components/mail/listing/conversations.rs index 8497ee0a..e6b98e4d 100644 --- a/src/components/mail/listing/conversations.rs +++ b/src/components/mail/listing/conversations.rs @@ -764,7 +764,7 @@ impl ConversationsListing { n / (24 * 60 * 60), if n / (24 * 60 * 60) == 1 { "" } else { "s" } ), - _ => melib::datetime::timestamp_to_string( + _ => melib::utils::datetime::timestamp_to_string( epoch, context .settings diff --git a/src/components/mail/listing/plain.rs b/src/components/mail/listing/plain.rs index b19dcae4..aaac5410 100644 --- a/src/components/mail/listing/plain.rs +++ b/src/components/mail/listing/plain.rs @@ -992,7 +992,7 @@ impl PlainListing { n if n < 4 * 24 * 60 * 60 => { format!("{} days ago{}", n / (24 * 60 * 60), " ".repeat(9)) } - _ => melib::datetime::timestamp_to_string(envelope.datetime(), None, false), + _ => melib::utils::datetime::timestamp_to_string(envelope.datetime(), None, false), } } diff --git a/src/components/mail/view.rs b/src/components/mail/view.rs index e001ffeb..83db95e0 100644 --- a/src/components/mail/view.rs +++ b/src/components/mail/view.rs @@ -28,8 +28,8 @@ use std::{ }; use melib::{ - datetime, email::attachment_types::ContentType, list_management, mailto::Mailto, - parser::BytesExt, Card, Draft, HeaderName, SpecialUsageMailbox, + email::attachment_types::ContentType, list_management, mailto::Mailto, parser::BytesExt, + utils::datetime, Card, Draft, HeaderName, SpecialUsageMailbox, }; use smallvec::SmallVec; diff --git a/src/components/svg.rs b/src/components/svg.rs index c5272442..fb493895 100644 --- a/src/components/svg.rs +++ b/src/components/svg.rs @@ -401,8 +401,8 @@ impl Component for SVGScreenshotFilter { } res.push(b); } - let mut filename = melib::datetime::timestamp_to_string( - melib::datetime::now(), + let mut filename = melib::utils::datetime::timestamp_to_string( + melib::utils::datetime::now(), Some("meli Screenshot - %e %h %Y %H:%M:%S.svg"), true, ); diff --git a/src/conf.rs b/src/conf.rs index 37f1a3aa..1b8d11b2 100644 --- a/src/conf.rs +++ b/src/conf.rs @@ -906,7 +906,7 @@ mod pp { use melib::{ error::{Error, Result}, - parsec::*, + utils::parsec::*, ShellExpandTrait, }; diff --git a/src/conf/accounts.rs b/src/conf/accounts.rs index a7e821ee..24009bc0 100644 --- a/src/conf/accounts.rs +++ b/src/conf/accounts.rs @@ -77,6 +77,15 @@ macro_rules! try_recv_timeout { }}; } +macro_rules! is_variant { + ($n:ident, $($var:tt)+) => { + #[inline] + pub fn $n(&self) -> bool { + matches!(self, Self::$($var)*) + } + }; +} + #[derive(Debug, Clone, Default)] pub enum MailboxStatus { Available, @@ -88,13 +97,8 @@ pub enum MailboxStatus { } impl MailboxStatus { - pub fn is_available(&self) -> bool { - matches!(self, MailboxStatus::Available) - } - - pub fn is_parsing(&self) -> bool { - matches!(self, MailboxStatus::Parsing(_, _)) - } + is_variant! { is_available, Available } + is_variant! { is_parsing, Parsing(_, _) } } #[derive(Debug, Clone)] diff --git a/src/sqlite3.rs b/src/sqlite3.rs index d880f6c0..57215db5 100644 --- a/src/sqlite3.rs +++ b/src/sqlite3.rs @@ -34,8 +34,8 @@ use melib::{ escape_double_quote, Query::{self, *}, }, - sqlite3::{self as melib_sqlite3, rusqlite::params, DatabaseDescription}, thread::{SortField, SortOrder}, + utils::sqlite3::{self as melib_sqlite3, rusqlite::params, DatabaseDescription}, Error, Result, }; use smallvec::SmallVec; @@ -497,7 +497,7 @@ mod tests { #[test] fn test_query_to_sql() { - use melib::{parsec::Parser, search::query}; + use melib::{search::query, utils::parsec::Parser}; assert_eq!( "(subject LIKE \"%test%\" ) AND (body_text LIKE \"%i%\" ) ", &query_to_sql(&query().parse_complete("subject:test and i").unwrap().1) diff --git a/src/state.rs b/src/state.rs index a8313522..5630c809 100644 --- a/src/state.rs +++ b/src/state.rs @@ -40,6 +40,7 @@ use crossbeam::channel::{unbounded, Receiver, Sender}; use indexmap::{IndexMap, IndexSet}; use melib::{ backends::{AccountHash, BackendEvent, BackendEventConsumer, Backends, RefreshEvent}, + utils::datetime, UnixTimestamp, }; use smallvec::SmallVec; @@ -542,7 +543,7 @@ impl State { let mut areas: smallvec::SmallVec<[Area; 8]> = self.context.dirty_areas.drain(0..).collect(); if self.display_messages_active { - let now = melib::datetime::now(); + let now = datetime::now(); if self .display_messages_expiration_start .map(|t| t + 5 < now) @@ -687,9 +688,11 @@ impl State { } } let ((x, mut y), box_displ_area_bottom_right) = box_displ_area; - for line in msg_lines.into_iter().chain(Some(String::new())).chain(Some( - melib::datetime::timestamp_to_string(*timestamp, None, false), - )) { + for line in msg_lines + .into_iter() + .chain(Some(String::new())) + .chain(Some(datetime::timestamp_to_string(*timestamp, None, false))) + { write_string_to_grid( &line, &mut self.screen.overlay_grid, @@ -1016,7 +1019,7 @@ impl State { pub fn rcv_event(&mut self, mut event: UIEvent) { if let UIEvent::Input(_) = event { if self.display_messages_expiration_start.is_none() { - self.display_messages_expiration_start = Some(melib::datetime::now()); + self.display_messages_expiration_start = Some(datetime::now()); } } @@ -1171,7 +1174,7 @@ impl State { .general .info_message_previous => { - self.display_messages_expiration_start = Some(melib::datetime::now()); + self.display_messages_expiration_start = Some(datetime::now()); self.display_messages_active = true; self.display_messages_initialised = false; self.display_messages_dirty = true; @@ -1181,7 +1184,7 @@ impl State { UIEvent::Input(ref key) if *key == self.context.settings.shortcuts.general.info_message_next => { - self.display_messages_expiration_start = Some(melib::datetime::now()); + self.display_messages_expiration_start = Some(datetime::now()); self.display_messages_active = true; self.display_messages_initialised = false; self.display_messages_dirty = true; @@ -1193,7 +1196,7 @@ impl State { } UIEvent::StatusEvent(StatusEvent::DisplayMessage(ref msg)) => { self.display_messages.push(DisplayMessage { - timestamp: melib::datetime::now(), + timestamp: datetime::now(), msg: msg.clone(), }); self.display_messages_active = true;