diff --git a/meli/config_macros.rs b/meli/config_macros.rs index 68e35840..2c4eff98 100644 --- a/meli/config_macros.rs +++ b/meli/config_macros.rs @@ -29,7 +29,7 @@ use quote::{format_ident, quote}; use regex::Regex; // Write ConfigStructOverride to overrides.rs -pub fn override_derive(filenames: &[(&str, &str)]) { +pub(crate) fn override_derive(filenames: &[(&str, &str)]) { let mut output_file = File::create("src/conf/overrides.rs").expect("Unable to open output file"); let mut output_string = r##"// @generated diff --git a/meli/src/components/mail/compose.rs b/meli/src/components/mail/compose.rs index 83e89404..4b9df9fa 100644 --- a/meli/src/components/mail/compose.rs +++ b/meli/src/components/mail/compose.rs @@ -40,7 +40,7 @@ use super::*; use crate::{conf::accounts::JobRequest, jobs::JoinHandle, terminal::embed::EmbedTerminal}; #[cfg(feature = "gpgme")] -mod gpg; +pub mod gpg; pub mod edit_attachments; use edit_attachments::*; diff --git a/meli/src/components/mail/listing.rs b/meli/src/components/mail/listing.rs index a4d97272..76ed3492 100644 --- a/meli/src/components/mail/listing.rs +++ b/meli/src/components/mail/listing.rs @@ -444,318 +444,339 @@ pub trait MailListingTrait: ListingTrait { envs_to_set: SmallVec<[EnvelopeHash; 8]>, a: &ListingAction, ) { - let account_hash = self.coordinates().0; - let account = &mut context.accounts[&account_hash]; - let mailbox_hash = self.coordinates().1; - /*{ - let threads_lck = account.collection.get_threads(mailbox_hash); - for thread_hash in thread_hashes { - for (_, h) in threads_lck.thread_iter(thread_hash) { - envs_to_set.push(threads_lck.thread_nodes()[&h].message().unwrap()); - } - self.row_updates().push(thread_hash); - } - } - */ - let env_hashes = if let Ok(batch) = EnvelopeHashBatch::try_from(envs_to_set.as_slice()) { - batch - } else { - return; - }; - match a { - ListingAction::SetSeen => { - let job = account.backend.write().unwrap().set_flags( - env_hashes.clone(), - mailbox_hash, - smallvec::smallvec![(Ok(Flag::SEEN), true)], - ); - match job { - Err(err) => { - context.replies.push_back(UIEvent::StatusEvent( - StatusEvent::DisplayMessage(err.to_string()), - )); - } - Ok(fut) => { - let handle = account - .main_loop_handler - .job_executor - .spawn_specialized("set_seen".into(), fut); - account - .insert_job(handle.job_id, JobRequest::SetFlags { env_hashes, handle }); + fn inner( + context: &mut Context, + envs_to_set: SmallVec<[EnvelopeHash; 8]>, + account_hash: AccountHash, + mailbox_hash: MailboxHash, + a: &ListingAction, + ) { + let env_hashes = if let Ok(batch) = EnvelopeHashBatch::try_from(envs_to_set.as_slice()) + { + batch + } else { + return; + }; + let account = &mut context.accounts[&account_hash]; + match a { + ListingAction::SetSeen => { + let job = account.backend.write().unwrap().set_flags( + env_hashes.clone(), + mailbox_hash, + smallvec::smallvec![(Ok(Flag::SEEN), true)], + ); + match job { + Err(err) => { + context.replies.push_back(UIEvent::StatusEvent( + StatusEvent::DisplayMessage(err.to_string()), + )); + } + Ok(fut) => { + let handle = account + .main_loop_handler + .job_executor + .spawn_specialized("set_seen".into(), fut); + account.insert_job( + handle.job_id, + JobRequest::SetFlags { env_hashes, handle }, + ); + } } } - } - ListingAction::SetUnseen => { - let job = account.backend.write().unwrap().set_flags( - env_hashes.clone(), - mailbox_hash, - smallvec::smallvec![(Ok(Flag::SEEN), false)], - ); - match job { - Err(err) => { - context.replies.push_back(UIEvent::StatusEvent( - StatusEvent::DisplayMessage(err.to_string()), - )); - } - Ok(fut) => { - let handle = account - .main_loop_handler - .job_executor - .spawn_specialized("set_unseen".into(), fut); - account - .insert_job(handle.job_id, JobRequest::SetFlags { env_hashes, handle }); + ListingAction::SetUnseen => { + let job = account.backend.write().unwrap().set_flags( + env_hashes.clone(), + mailbox_hash, + smallvec::smallvec![(Ok(Flag::SEEN), false)], + ); + match job { + Err(err) => { + context.replies.push_back(UIEvent::StatusEvent( + StatusEvent::DisplayMessage(err.to_string()), + )); + } + Ok(fut) => { + let handle = account + .main_loop_handler + .job_executor + .spawn_specialized("set_unseen".into(), fut); + account.insert_job( + handle.job_id, + JobRequest::SetFlags { env_hashes, handle }, + ); + } } } - } - ListingAction::Tag(Remove(ref tag_str)) => { - let job = account.backend.write().unwrap().set_flags( - env_hashes.clone(), - mailbox_hash, - smallvec::smallvec![(Err(tag_str.to_string()), false)], - ); - match job { - Err(err) => { - context.replies.push_back(UIEvent::StatusEvent( - StatusEvent::DisplayMessage(err.to_string()), - )); - } - Ok(fut) => { - let handle = account - .main_loop_handler - .job_executor - .spawn_specialized("remove_tag".into(), fut); - account - .insert_job(handle.job_id, JobRequest::SetFlags { env_hashes, handle }); + ListingAction::Tag(Remove(ref tag_str)) => { + let job = account.backend.write().unwrap().set_flags( + env_hashes.clone(), + mailbox_hash, + smallvec::smallvec![(Err(tag_str.to_string()), false)], + ); + match job { + Err(err) => { + context.replies.push_back(UIEvent::StatusEvent( + StatusEvent::DisplayMessage(err.to_string()), + )); + } + Ok(fut) => { + let handle = account + .main_loop_handler + .job_executor + .spawn_specialized("remove_tag".into(), fut); + account.insert_job( + handle.job_id, + JobRequest::SetFlags { env_hashes, handle }, + ); + } } } - } - ListingAction::Tag(Add(ref tag_str)) => { - let job = account.backend.write().unwrap().set_flags( - env_hashes.clone(), - mailbox_hash, - smallvec::smallvec![(Err(tag_str.to_string()), true)], - ); - match job { - Err(err) => { - context.replies.push_back(UIEvent::StatusEvent( - StatusEvent::DisplayMessage(err.to_string()), - )); - } - Ok(fut) => { - let handle = account - .main_loop_handler - .job_executor - .spawn_specialized("add_tag".into(), fut); - account - .insert_job(handle.job_id, JobRequest::SetFlags { env_hashes, handle }); + ListingAction::Tag(Add(ref tag_str)) => { + let job = account.backend.write().unwrap().set_flags( + env_hashes.clone(), + mailbox_hash, + smallvec::smallvec![(Err(tag_str.to_string()), true)], + ); + match job { + Err(err) => { + context.replies.push_back(UIEvent::StatusEvent( + StatusEvent::DisplayMessage(err.to_string()), + )); + } + Ok(fut) => { + let handle = account + .main_loop_handler + .job_executor + .spawn_specialized("add_tag".into(), fut); + account.insert_job( + handle.job_id, + JobRequest::SetFlags { env_hashes, handle }, + ); + } } } - } - ListingAction::Delete => { - let job = account - .backend - .write() - .unwrap() - .delete_messages(env_hashes.clone(), mailbox_hash); - match job { - Err(err) => { - context.replies.push_back(UIEvent::StatusEvent( - StatusEvent::DisplayMessage(err.to_string()), - )); - } - Ok(fut) => { - let handle = account - .main_loop_handler - .job_executor - .spawn_specialized("delete".into(), fut); - account.insert_job( - handle.job_id, - JobRequest::DeleteMessages { env_hashes, handle }, - ); + ListingAction::Delete => { + let job = account + .backend + .write() + .unwrap() + .delete_messages(env_hashes.clone(), mailbox_hash); + match job { + Err(err) => { + context.replies.push_back(UIEvent::StatusEvent( + StatusEvent::DisplayMessage(err.to_string()), + )); + } + Ok(fut) => { + let handle = account + .main_loop_handler + .job_executor + .spawn_specialized("delete".into(), fut); + account.insert_job( + handle.job_id, + JobRequest::DeleteMessages { env_hashes, handle }, + ); + } } } - } - ListingAction::CopyTo(ref mailbox_path) => { - match account - .mailbox_by_path(mailbox_path) - .and_then(|destination_mailbox_hash| { - account.backend.write().unwrap().copy_messages( - env_hashes, - mailbox_hash, - destination_mailbox_hash, - /* move? */ false, - ) - }) { - Err(err) => { - context.replies.push_back(UIEvent::StatusEvent( - StatusEvent::DisplayMessage(err.to_string()), - )); - } - Ok(fut) => { - let handle = account - .main_loop_handler - .job_executor - .spawn_specialized("copy_to_mailbox".into(), fut); - account.insert_job( - handle.job_id, - JobRequest::Generic { - name: "message copying".into(), - handle, - on_finish: None, - log_level: LogLevel::INFO, - }, - ); + ListingAction::CopyTo(ref mailbox_path) => { + match account.mailbox_by_path(mailbox_path).and_then( + |destination_mailbox_hash| { + account.backend.write().unwrap().copy_messages( + env_hashes, + mailbox_hash, + destination_mailbox_hash, + /* move? */ false, + ) + }, + ) { + Err(err) => { + context.replies.push_back(UIEvent::StatusEvent( + StatusEvent::DisplayMessage(err.to_string()), + )); + } + Ok(fut) => { + let handle = account + .main_loop_handler + .job_executor + .spawn_specialized("copy_to_mailbox".into(), fut); + account.insert_job( + handle.job_id, + JobRequest::Generic { + name: "message copying".into(), + handle, + on_finish: None, + log_level: LogLevel::INFO, + }, + ); + } } } - } - ListingAction::CopyToOtherAccount(ref _account_name, ref _mailbox_path) => { - context - .replies - .push_back(UIEvent::StatusEvent(StatusEvent::DisplayMessage( - "Copying to another account is currently unimplemented".into(), - ))); - } - ListingAction::MoveTo(ref mailbox_path) => { - match account - .mailbox_by_path(mailbox_path) - .and_then(|destination_mailbox_hash| { - account.backend.write().unwrap().copy_messages( - env_hashes, - mailbox_hash, - destination_mailbox_hash, - /* move? */ true, - ) - }) { - Err(err) => { - context.replies.push_back(UIEvent::StatusEvent( - StatusEvent::DisplayMessage(err.to_string()), - )); - } - Ok(fut) => { - let handle = account - .main_loop_handler - .job_executor - .spawn_specialized("move_to_mailbox".into(), fut); - account.insert_job( - handle.job_id, - JobRequest::Generic { - name: "message moving".into(), - handle, - on_finish: None, - log_level: LogLevel::INFO, - }, - ); + ListingAction::CopyToOtherAccount(ref _account_name, ref _mailbox_path) => { + context + .replies + .push_back(UIEvent::StatusEvent(StatusEvent::DisplayMessage( + "Copying to another account is currently unimplemented".into(), + ))); + } + ListingAction::MoveTo(ref mailbox_path) => { + match account.mailbox_by_path(mailbox_path).and_then( + |destination_mailbox_hash| { + account.backend.write().unwrap().copy_messages( + env_hashes, + mailbox_hash, + destination_mailbox_hash, + /* move? */ true, + ) + }, + ) { + Err(err) => { + context.replies.push_back(UIEvent::StatusEvent( + StatusEvent::DisplayMessage(err.to_string()), + )); + } + Ok(fut) => { + let handle = account + .main_loop_handler + .job_executor + .spawn_specialized("move_to_mailbox".into(), fut); + account.insert_job( + handle.job_id, + JobRequest::Generic { + name: "message moving".into(), + handle, + on_finish: None, + log_level: LogLevel::INFO, + }, + ); + } } } - } - ListingAction::ExportMbox(format, ref path) => { - use std::{future::Future, io::Write, pin::Pin}; + ListingAction::ExportMbox(format, ref path) => { + use std::{future::Future, io::Write, pin::Pin}; - use futures::future::try_join_all; + use futures::future::try_join_all; - let futures: Result> = envs_to_set - .iter() - .map(|&env_hash| account.operation(env_hash).and_then(|mut op| op.as_bytes())) - .collect::>>(); - let path_ = path.to_path_buf(); - let format = (*format).unwrap_or_default(); - let collection = account.collection.clone(); - let (sender, mut receiver) = crate::jobs::oneshot::channel(); - let fut: Pin> + Send + 'static>> = - Box::pin(async move { - let cl = async move { - use melib::backends::mbox::MboxMetadata; - let bytes: Vec> = try_join_all(futures?).await?; - let envs: Vec<_> = envs_to_set - .iter() - .map(|&env_hash| collection.get_env(env_hash)) - .collect(); - let mut file = std::io::BufWriter::new(std::fs::File::create(&path_)?); - let mut iter = envs.iter().zip(bytes.into_iter()); - let tags_lck = collection.tag_index.read().unwrap(); - if let Some((env, ref bytes)) = iter.next() { - let tags: Vec<&str> = env - .tags() - .iter() - .filter_map(|h| tags_lck.get(h).map(|s| s.as_str())) - .collect(); - format.append( - &mut file, - bytes.as_slice(), - env.from().get(0), - Some(env.date()), - (env.flags(), tags), - MboxMetadata::CClient, - true, - false, - )?; - } - for (env, bytes) in iter { - let tags: Vec<&str> = env - .tags() + let futures: Result> = envs_to_set + .iter() + .map(|&env_hash| { + account.operation(env_hash).and_then(|mut op| op.as_bytes()) + }) + .collect::>>(); + let path_ = path.to_path_buf(); + let format = (*format).unwrap_or_default(); + let collection = account.collection.clone(); + let (sender, mut receiver) = crate::jobs::oneshot::channel(); + let fut: Pin> + Send + 'static>> = + Box::pin(async move { + let cl = async move { + use melib::backends::mbox::MboxMetadata; + let bytes: Vec> = try_join_all(futures?).await?; + let envs: Vec<_> = envs_to_set .iter() - .filter_map(|h| tags_lck.get(h).map(|s| s.as_str())) + .map(|&env_hash| collection.get_env(env_hash)) .collect(); - format.append( - &mut file, - bytes.as_slice(), - env.from().get(0), - Some(env.date()), - (env.flags(), tags), - MboxMetadata::CClient, - false, - false, - )?; - } - file.flush()?; + let mut file = + std::io::BufWriter::new(std::fs::File::create(&path_)?); + let mut iter = envs.iter().zip(bytes.into_iter()); + let tags_lck = collection.tag_index.read().unwrap(); + if let Some((env, ref bytes)) = iter.next() { + let tags: Vec<&str> = env + .tags() + .iter() + .filter_map(|h| tags_lck.get(h).map(|s| s.as_str())) + .collect(); + format.append( + &mut file, + bytes.as_slice(), + env.from().get(0), + Some(env.date()), + (env.flags(), tags), + MboxMetadata::CClient, + true, + false, + )?; + } + for (env, bytes) in iter { + let tags: Vec<&str> = env + .tags() + .iter() + .filter_map(|h| tags_lck.get(h).map(|s| s.as_str())) + .collect(); + format.append( + &mut file, + bytes.as_slice(), + env.from().get(0), + Some(env.date()), + (env.flags(), tags), + MboxMetadata::CClient, + false, + false, + )?; + } + file.flush()?; + Ok(()) + }; + let r: Result<()> = cl.await; + let _ = sender.send(r); Ok(()) - }; - let r: Result<()> = cl.await; - let _ = sender.send(r); - Ok(()) - }); - let handle = account - .main_loop_handler - .job_executor - .spawn_blocking("export_to_mbox".into(), fut); - let path = path.to_path_buf(); - account.insert_job( - handle.job_id, - JobRequest::Generic { - name: "exporting mbox".into(), - handle, - on_finish: Some(CallbackFn(Box::new(move |context: &mut Context| { - context.replies.push_back(match receiver.try_recv() { - Err(_) | Ok(None) => UIEvent::Notification( - Some("Could not export mbox".to_string()), - "Job was canceled.".to_string(), - Some(NotificationType::Info), - ), - Ok(Some(Err(err))) => UIEvent::Notification( - Some("Could not export mbox".to_string()), - err.to_string(), - Some(NotificationType::Error(err.kind)), - ), - Ok(Some(Ok(()))) => UIEvent::Notification( - Some("Succesfully exported mbox".to_string()), - format!("Wrote to file {}", path.display()), - Some(NotificationType::Info), - ), - }); - }))), - log_level: LogLevel::INFO, - }, - ); + }); + let handle = account + .main_loop_handler + .job_executor + .spawn_blocking("export_to_mbox".into(), fut); + let path = path.to_path_buf(); + account.insert_job( + handle.job_id, + JobRequest::Generic { + name: "exporting mbox".into(), + handle, + on_finish: Some(CallbackFn(Box::new(move |context: &mut Context| { + context.replies.push_back(match receiver.try_recv() { + Err(_) | Ok(None) => UIEvent::Notification( + Some("Could not export mbox".to_string()), + "Job was canceled.".to_string(), + Some(NotificationType::Info), + ), + Ok(Some(Err(err))) => UIEvent::Notification( + Some("Could not export mbox".to_string()), + err.to_string(), + Some(NotificationType::Error(err.kind)), + ), + Ok(Some(Ok(()))) => UIEvent::Notification( + Some("Succesfully exported mbox".to_string()), + format!("Wrote to file {}", path.display()), + Some(NotificationType::Info), + ), + }); + }))), + log_level: LogLevel::INFO, + }, + ); + } + ListingAction::MoveToOtherAccount(ref _account_name, ref _mailbox_path) => { + context + .replies + .push_back(UIEvent::StatusEvent(StatusEvent::DisplayMessage( + "Moving to another account is currently unimplemented".into(), + ))); + } + _ => unreachable!(), } - ListingAction::MoveToOtherAccount(ref _account_name, ref _mailbox_path) => { - context - .replies - .push_back(UIEvent::StatusEvent(StatusEvent::DisplayMessage( - "Moving to another account is currently unimplemented".into(), - ))); + } + let account_hash = self.coordinates().0; + let mailbox_hash = self.coordinates().1; + /*{ + let threads_lck = account.collection.get_threads(mailbox_hash); + for thread_hash in thread_hashes { + for (_, h) in threads_lck.thread_iter(thread_hash) { + envs_to_set.push(threads_lck.thread_nodes()[&h].message().unwrap()); + } + self.row_updates().push(thread_hash); } - _ => unreachable!(), } + */ + inner(context, envs_to_set, account_hash, mailbox_hash, a); self.set_dirty(true); } diff --git a/meli/src/components/mail/view.rs b/meli/src/components/mail/view.rs index fd08a65b..99ae13ec 100644 --- a/meli/src/components/mail/view.rs +++ b/meli/src/components/mail/view.rs @@ -45,7 +45,7 @@ mod thread; pub use thread::*; mod types; pub use types::*; -mod state; +pub mod state; use state::*; pub mod envelope; diff --git a/meli/src/conf.rs b/meli/src/conf.rs index 2e834150..61f03a9b 100644 --- a/meli/src/conf.rs +++ b/meli/src/conf.rs @@ -1010,7 +1010,7 @@ mod pp { /// Expands `include` macros in configuration file and other configuration /// files (eg. themes) in the filesystem. - pub fn pp>(path: P) -> Result { + pub(super) fn pp>(path: P) -> Result { let p_buf: PathBuf = if path.as_ref().is_relative() { path.as_ref().expand().canonicalize()? } else { diff --git a/meli/src/conf/accounts.rs b/meli/src/conf/accounts.rs index 96ea61d1..7b6a8291 100644 --- a/meli/src/conf/accounts.rs +++ b/meli/src/conf/accounts.rs @@ -466,6 +466,8 @@ impl Account { let backend = map.get(&settings.account().format)( settings.account(), Box::new(move |path: &str| { + // disjoint-capture-in-closures + let _ = &s; s.account.subscribed_mailboxes.is_empty() || (s.mailbox_confs.contains_key(path) && s.mailbox_confs[path].mailbox_conf().subscribe.is_true()) diff --git a/meli/src/conf/composing.rs b/meli/src/conf/composing.rs index 77ce3d0b..1b9d66cc 100644 --- a/meli/src/conf/composing.rs +++ b/meli/src/conf/composing.rs @@ -171,7 +171,7 @@ macro_rules! named_unit_variant { }; } -mod strings { +pub mod strings { named_unit_variant!(server_submission); } diff --git a/meli/src/jobs.rs b/meli/src/jobs.rs index 71593e70..686b305e 100644 --- a/meli/src/jobs.rs +++ b/meli/src/jobs.rs @@ -44,7 +44,11 @@ use crate::types::{ThreadEvent, UIEvent}; type AsyncTask = async_task::Runnable; -fn find_task(local: &Worker, global: &Injector, stealers: &[Stealer]) -> Option { +fn find_task( + local: &Worker, + global: &Injector, + stealers: &[Stealer], +) -> Option { // Pop a task from the local queue, if not empty. local.pop().or_else(|| { // Otherwise, we need to look for a task elsewhere. diff --git a/meli/src/terminal.rs b/meli/src/terminal.rs index ab3089e4..3aeb32b9 100644 --- a/meli/src/terminal.rs +++ b/meli/src/terminal.rs @@ -26,13 +26,13 @@ use serde::{de, de::Visitor, Deserialize, Deserializer}; mod color; pub use color::*; #[macro_use] -mod position; +pub mod position; #[macro_use] -mod cells; +pub mod cells; #[macro_use] -mod keys; +pub mod keys; pub mod embed; -mod text_editing; +pub mod text_editing; use std::fmt; pub use self::{cells::*, keys::*, position::*, text_editing::*}; diff --git a/meli/src/terminal/color.rs b/meli/src/terminal/color.rs index 67f33aaf..cefff521 100644 --- a/meli/src/terminal/color.rs +++ b/meli/src/terminal/color.rs @@ -746,7 +746,6 @@ impl Serialize for Color { } } -pub use aliases::*; pub mod aliases { use super::Color; diff --git a/melib/src/backends/imap.rs b/melib/src/backends/imap.rs index e2b07cf2..7f237724 100644 --- a/melib/src/backends/imap.rs +++ b/melib/src/backends/imap.rs @@ -34,10 +34,10 @@ mod watch; pub use watch::*; mod search; pub use search::*; -mod cache; +pub mod cache; pub mod error; pub mod managesieve; -mod untagged; +pub mod untagged; use std::{ collections::{hash_map::DefaultHasher, BTreeSet, HashMap, HashSet}, diff --git a/melib/src/backends/imap/cache.rs b/melib/src/backends/imap/cache.rs index 29aa7abf..4cdc1b74 100644 --- a/melib/src/backends/imap/cache.rs +++ b/melib/src/backends/imap/cache.rs @@ -20,7 +20,7 @@ */ use super::*; -mod sync; +pub mod sync; use std::convert::TryFrom; use crate::{ @@ -104,7 +104,7 @@ pub trait ImapCacheReset: Send + core::fmt::Debug { pub use sqlite3_m::*; #[cfg(feature = "sqlite3")] -mod sqlite3_m { +pub mod sqlite3_m { use super::*; use crate::utils::sqlite3::{ self, @@ -739,7 +739,7 @@ pub(super) async fn fetch_cached_envs(state: &mut FetchState) -> Result Result> { - let (_, ret) = separated_list1( - tag(b"\r\n"), - alt(( - separated_pair(quoted_raw, tag(b" "), quoted_raw), - map(quoted_raw, |q| (q, &b""[..])), - )), - )(input)?; - Ok(ret) -} - #[derive(PartialEq, Eq, Debug, Copy, Clone)] pub enum ManageSieveResponse<'a> { Ok { @@ -66,231 +50,6 @@ pub enum ManageSieveResponse<'a> { }, } -mod parser { - use nom::{ - bytes::complete::tag, - character::complete::crlf, - combinator::{iterator, map, opt}, - }; - pub use nom::{ - bytes::complete::{is_not, tag_no_case}, - sequence::{delimited, pair, preceded, terminated}, - }; - - use super::*; - - pub fn sieve_name(input: &[u8]) -> IResult<&[u8], &[u8]> { - crate::backends::imap::protocol_parser::string_token(input) - } - - // *(sieve-name [SP "ACTIVE"] CRLF) - // response-oknobye - pub fn listscripts(input: &[u8]) -> IResult<&[u8], Vec<(&[u8], bool)>> { - let mut it = iterator( - input, - alt(( - terminated( - map(terminated(sieve_name, tag_no_case(b" ACTIVE")), |r| { - (r, true) - }), - crlf, - ), - terminated(map(sieve_name, |r| (r, false)), crlf), - )), - ); - - let parsed = (&mut it).collect::>(); - let res: IResult<_, _> = it.finish(); - let (rest, _) = res?; - Ok((rest, parsed)) - } - - // response-getscript = (sieve-script CRLF response-ok) / - // response-nobye - pub fn getscript(input: &[u8]) -> IResult<&[u8], &[u8]> { - sieve_name(input) - } - - pub fn response_oknobye(input: &[u8]) -> IResult<&[u8], ManageSieveResponse> { - alt(( - map( - terminated( - pair( - preceded( - tag_no_case(b"ok"), - opt(preceded( - tag(b" "), - delimited(tag(b"("), is_not(")"), tag(b")")), - )), - ), - opt(preceded(tag(b" "), sieve_name)), - ), - crlf, - ), - |(code, message)| ManageSieveResponse::Ok { code, message }, - ), - map( - terminated( - pair( - preceded( - alt((tag_no_case(b"no"), tag_no_case(b"bye"))), - opt(preceded( - tag(b" "), - delimited(tag(b"("), is_not(")"), tag(b")")), - )), - ), - opt(preceded(tag(b" "), sieve_name)), - ), - crlf, - ), - |(code, message)| ManageSieveResponse::NoBye { code, message }, - ), - ))(input) - } - - #[test] - fn test_managesieve_listscripts() { - let input_1 = b"\"summer_script\"\r\n\"vacation_script\"\r\n{13}\r\nclever\"script\r\n\"main_script\" ACTIVE\r\nOK"; - assert_eq!( - terminated(listscripts, tag_no_case(b"OK"))(input_1), - Ok(( - &b""[..], - vec![ - (&b"summer_script"[..], false), - (&b"vacation_script"[..], false), - (&b"clever\"script"[..], false), - (&b"main_script"[..], true) - ] - )) - ); - - let input_2 = b"\"summer_script\"\r\n\"main_script\" active\r\nok"; - assert_eq!( - terminated(listscripts, tag_no_case(b"OK"))(input_2), - Ok(( - &b""[..], - vec![(&b"summer_script"[..], false), (&b"main_script"[..], true)] - )) - ); - let input_3 = b"ok"; - assert_eq!( - terminated(listscripts, tag_no_case(b"OK"))(input_3), - Ok((&b""[..], vec![])) - ); - } - - #[test] - fn test_managesieve_general() { - assert_eq!(managesieve_capabilities(b"\"IMPLEMENTATION\" \"Dovecot Pigeonhole\"\r\n\"SIEVE\" \"fileinto reject envelope encoded-character vacation subaddress comparator-i;ascii-numeric relational regex imap4flags copy include variables body enotify environment mailbox date index ihave duplicate mime foreverypart extracttext\"\r\n\"NOTIFY\" \"mailto\"\r\n\"SASL\" \"PLAIN\"\r\n\"STARTTLS\"\r\n\"VERSION\" \"1.0\"\r\n").unwrap(), vec![ - (&b"IMPLEMENTATION"[..],&b"Dovecot Pigeonhole"[..]), - (&b"SIEVE"[..],&b"fileinto reject envelope encoded-character vacation subaddress comparator-i;ascii-numeric relational regex imap4flags copy include variables body enotify environment mailbox date index ihave duplicate mime foreverypart extracttext"[..]), - (&b"NOTIFY"[..],&b"mailto"[..]), - (&b"SASL"[..],&b"PLAIN"[..]), - (&b"STARTTLS"[..], &b""[..]), - (&b"VERSION"[..],&b"1.0"[..])] - - ); - - let response_ok = b"OK (WARNINGS) \"line 8: server redirect action limit is 2, this redirect might be ignored\"\r\n"; - assert_eq!( - response_oknobye(response_ok), - Ok(( - &b""[..], - ManageSieveResponse::Ok { - code: Some(&b"WARNINGS"[..]), - message: Some(&b"line 8: server redirect action limit is 2, this redirect might be ignored"[..]), - } - )) - ); - let response_ok = b"OK (WARNINGS)\r\n"; - assert_eq!( - response_oknobye(response_ok), - Ok(( - &b""[..], - ManageSieveResponse::Ok { - code: Some(&b"WARNINGS"[..]), - message: None, - } - )) - ); - let response_ok = - b"OK \"line 8: server redirect action limit is 2, this redirect might be ignored\"\r\n"; - assert_eq!( - response_oknobye(response_ok), - Ok(( - &b""[..], - ManageSieveResponse::Ok { - code: None, - message: Some(&b"line 8: server redirect action limit is 2, this redirect might be ignored"[..]), - } - )) - ); - let response_ok = b"Ok\r\n"; - assert_eq!( - response_oknobye(response_ok), - Ok(( - &b""[..], - ManageSieveResponse::Ok { - code: None, - message: None, - } - )) - ); - - let response_nobye = b"No (NONEXISTENT) \"There is no script by that name\"\r\n"; - assert_eq!( - response_oknobye(response_nobye), - Ok(( - &b""[..], - ManageSieveResponse::NoBye { - code: Some(&b"NONEXISTENT"[..]), - message: Some(&b"There is no script by that name"[..]), - } - )) - ); - let response_nobye = b"No (NONEXISTENT) {31}\r\nThere is no script by that name\r\n"; - assert_eq!( - response_oknobye(response_nobye), - Ok(( - &b""[..], - ManageSieveResponse::NoBye { - code: Some(&b"NONEXISTENT"[..]), - message: Some(&b"There is no script by that name"[..]), - } - )) - ); - - let response_nobye = b"No\r\n"; - assert_eq!( - response_oknobye(response_nobye), - Ok(( - &b""[..], - ManageSieveResponse::NoBye { - code: None, - message: None, - } - )) - ); - } -} - -// Return a byte sequence surrounded by "s and decoded if necessary -pub fn quoted_raw(input: &[u8]) -> IResult<&[u8], &[u8]> { - if input.is_empty() || input[0] != b'"' { - return Err(nom::Err::Error((input, "empty").into())); - } - - let mut i = 1; - while i < input.len() { - if input[i] == b'\"' && input[i - 1] != b'\\' { - return Ok((&input[i + 1..], &input[1..i])); - } - i += 1; - } - - Err(nom::Err::Error((input, "no quotes").into())) -} - impl ManageSieveConnection { pub fn new( account_hash: crate::backends::AccountHash, @@ -479,3 +238,247 @@ impl ManageSieveConnection { Ok(()) } } + +pub mod parser { + use nom::{ + branch::alt, + bytes::complete::tag, + character::complete::crlf, + combinator::{iterator, map, opt}, + multi::separated_list1, + sequence::separated_pair, + }; + pub use nom::{ + bytes::complete::{is_not, tag_no_case}, + sequence::{delimited, pair, preceded, terminated}, + }; + + use super::*; + + /// Return a byte sequence surrounded by "s and decoded if necessary + pub fn quoted_raw(input: &[u8]) -> IResult<&[u8], &[u8]> { + if input.is_empty() || input[0] != b'"' { + return Err(nom::Err::Error((input, "empty").into())); + } + + let mut i = 1; + while i < input.len() { + if input[i] == b'\"' && input[i - 1] != b'\\' { + return Ok((&input[i + 1..], &input[1..i])); + } + i += 1; + } + + Err(nom::Err::Error((input, "no quotes").into())) + } + + pub fn managesieve_capabilities(input: &[u8]) -> Result> { + let (_, ret) = separated_list1( + tag(b"\r\n"), + alt(( + separated_pair(quoted_raw, tag(b" "), quoted_raw), + map(quoted_raw, |q| (q, &b""[..])), + )), + )(input)?; + Ok(ret) + } + + pub fn sieve_name(input: &[u8]) -> IResult<&[u8], &[u8]> { + crate::backends::imap::protocol_parser::string_token(input) + } + + // *(sieve-name [SP "ACTIVE"] CRLF) + // response-oknobye + pub fn listscripts(input: &[u8]) -> IResult<&[u8], Vec<(&[u8], bool)>> { + let mut it = iterator( + input, + alt(( + terminated( + map(terminated(sieve_name, tag_no_case(b" ACTIVE")), |r| { + (r, true) + }), + crlf, + ), + terminated(map(sieve_name, |r| (r, false)), crlf), + )), + ); + + let parsed = (&mut it).collect::>(); + let res: IResult<_, _> = it.finish(); + let (rest, _) = res?; + Ok((rest, parsed)) + } + + // response-getscript = (sieve-script CRLF response-ok) / + // response-nobye + pub fn getscript(input: &[u8]) -> IResult<&[u8], &[u8]> { + sieve_name(input) + } + + pub fn response_oknobye(input: &[u8]) -> IResult<&[u8], ManageSieveResponse> { + alt(( + map( + terminated( + pair( + preceded( + tag_no_case(b"ok"), + opt(preceded( + tag(b" "), + delimited(tag(b"("), is_not(")"), tag(b")")), + )), + ), + opt(preceded(tag(b" "), sieve_name)), + ), + crlf, + ), + |(code, message)| ManageSieveResponse::Ok { code, message }, + ), + map( + terminated( + pair( + preceded( + alt((tag_no_case(b"no"), tag_no_case(b"bye"))), + opt(preceded( + tag(b" "), + delimited(tag(b"("), is_not(")"), tag(b")")), + )), + ), + opt(preceded(tag(b" "), sieve_name)), + ), + crlf, + ), + |(code, message)| ManageSieveResponse::NoBye { code, message }, + ), + ))(input) + } + + #[cfg(test)] + mod tests { + use super::*; + + #[test] + fn test_managesieve_listscripts() { + let input_1 = b"\"summer_script\"\r\n\"vacation_script\"\r\n{13}\r\nclever\"script\r\n\"main_script\" ACTIVE\r\nOK"; + assert_eq!( + terminated(listscripts, tag_no_case(b"OK"))(input_1), + Ok(( + &b""[..], + vec![ + (&b"summer_script"[..], false), + (&b"vacation_script"[..], false), + (&b"clever\"script"[..], false), + (&b"main_script"[..], true) + ] + )) + ); + + let input_2 = b"\"summer_script\"\r\n\"main_script\" active\r\nok"; + assert_eq!( + terminated(listscripts, tag_no_case(b"OK"))(input_2), + Ok(( + &b""[..], + vec![(&b"summer_script"[..], false), (&b"main_script"[..], true)] + )) + ); + let input_3 = b"ok"; + assert_eq!( + terminated(listscripts, tag_no_case(b"OK"))(input_3), + Ok((&b""[..], vec![])) + ); + } + + #[test] + fn test_managesieve_general() { + assert_eq!(managesieve_capabilities(b"\"IMPLEMENTATION\" \"Dovecot Pigeonhole\"\r\n\"SIEVE\" \"fileinto reject envelope encoded-character vacation subaddress comparator-i;ascii-numeric relational regex imap4flags copy include variables body enotify environment mailbox date index ihave duplicate mime foreverypart extracttext\"\r\n\"NOTIFY\" \"mailto\"\r\n\"SASL\" \"PLAIN\"\r\n\"STARTTLS\"\r\n\"VERSION\" \"1.0\"\r\n").unwrap(), vec![ + (&b"IMPLEMENTATION"[..],&b"Dovecot Pigeonhole"[..]), + (&b"SIEVE"[..],&b"fileinto reject envelope encoded-character vacation subaddress comparator-i;ascii-numeric relational regex imap4flags copy include variables body enotify environment mailbox date index ihave duplicate mime foreverypart extracttext"[..]), + (&b"NOTIFY"[..],&b"mailto"[..]), + (&b"SASL"[..],&b"PLAIN"[..]), + (&b"STARTTLS"[..], &b""[..]), + (&b"VERSION"[..],&b"1.0"[..])] + + ); + + let response_ok = b"OK (WARNINGS) \"line 8: server redirect action limit is 2, this redirect might be ignored\"\r\n"; + assert_eq!( + response_oknobye(response_ok), + Ok(( + &b""[..], + ManageSieveResponse::Ok { + code: Some(&b"WARNINGS"[..]), + message: Some(&b"line 8: server redirect action limit is 2, this redirect might be ignored"[..]), + } + )) + ); + let response_ok = b"OK (WARNINGS)\r\n"; + assert_eq!( + response_oknobye(response_ok), + Ok(( + &b""[..], + ManageSieveResponse::Ok { + code: Some(&b"WARNINGS"[..]), + message: None, + } + )) + ); + let response_ok = + b"OK \"line 8: server redirect action limit is 2, this redirect might be ignored\"\r\n"; + assert_eq!( + response_oknobye(response_ok), + Ok(( + &b""[..], + ManageSieveResponse::Ok { + code: None, + message: Some(&b"line 8: server redirect action limit is 2, this redirect might be ignored"[..]), + } + )) + ); + let response_ok = b"Ok\r\n"; + assert_eq!( + response_oknobye(response_ok), + Ok(( + &b""[..], + ManageSieveResponse::Ok { + code: None, + message: None, + } + )) + ); + + let response_nobye = b"No (NONEXISTENT) \"There is no script by that name\"\r\n"; + assert_eq!( + response_oknobye(response_nobye), + Ok(( + &b""[..], + ManageSieveResponse::NoBye { + code: Some(&b"NONEXISTENT"[..]), + message: Some(&b"There is no script by that name"[..]), + } + )) + ); + let response_nobye = b"No (NONEXISTENT) {31}\r\nThere is no script by that name\r\n"; + assert_eq!( + response_oknobye(response_nobye), + Ok(( + &b""[..], + ManageSieveResponse::NoBye { + code: Some(&b"NONEXISTENT"[..]), + message: Some(&b"There is no script by that name"[..]), + } + )) + ); + + let response_nobye = b"No\r\n"; + assert_eq!( + response_oknobye(response_nobye), + Ok(( + &b""[..], + ManageSieveResponse::NoBye { + code: None, + message: None, + } + )) + ); + } + } +} diff --git a/melib/src/backends/mbox.rs b/melib/src/backends/mbox.rs index 8d19f17a..2d0546f2 100644 --- a/melib/src/backends/mbox.rs +++ b/melib/src/backends/mbox.rs @@ -202,20 +202,20 @@ fn get_rw_lock_blocking(f: &File, path: &Path) -> Result<()> { } #[derive(Debug)] -struct MboxMailbox { - hash: MailboxHash, - name: String, - path: PathBuf, - fs_path: PathBuf, - content: Vec, - children: Vec, - parent: Option, - usage: Arc>, - is_subscribed: bool, - permissions: MailboxPermissions, +pub struct MboxMailbox { + pub hash: MailboxHash, + pub name: String, + pub path: PathBuf, + pub fs_path: PathBuf, + pub content: Vec, + pub children: Vec, + pub parent: Option, + pub usage: Arc>, + pub is_subscribed: bool, + pub permissions: MailboxPermissions, pub total: Arc>, pub unseen: Arc>, - index: Arc>>, + pub index: Arc>>, } impl BackendMailbox for MboxMailbox { @@ -285,11 +285,11 @@ impl BackendMailbox for MboxMailbox { /// `BackendOp` implementor for Mbox #[derive(Debug, Default)] pub struct MboxOp { - _hash: EnvelopeHash, - path: PathBuf, - offset: Offset, - length: Length, - slice: std::cell::RefCell>>, + pub _hash: EnvelopeHash, + pub path: PathBuf, + pub offset: Offset, + pub length: Length, + pub slice: std::cell::RefCell>>, } impl MboxOp { diff --git a/melib/src/backends/nntp.rs b/melib/src/backends/nntp.rs index c5c55fd8..41a6e94e 100644 --- a/melib/src/backends/nntp.rs +++ b/melib/src/backends/nntp.rs @@ -30,6 +30,7 @@ use smallvec::SmallVec; use crate::{get_conf_val, get_path_hash}; mod store; +pub use store::*; #[macro_use] mod protocol_parser; pub use protocol_parser::*; diff --git a/melib/src/backends/nntp/store.rs b/melib/src/backends/nntp/store.rs index 5064ce8e..8b740170 100644 --- a/melib/src/backends/nntp/store.rs +++ b/melib/src/backends/nntp/store.rs @@ -55,7 +55,7 @@ CREATE TABLE IF NOT EXISTS article ( #[derive(Debug)] pub struct Store { - connection: Connection, + pub connection: Connection, } impl Store { diff --git a/melib/src/backends/notmuch.rs b/melib/src/backends/notmuch.rs index af95a022..037b8ce3 100644 --- a/melib/src/backends/notmuch.rs +++ b/melib/src/backends/notmuch.rs @@ -718,7 +718,7 @@ impl MailBackend for NotmuchDb { use notify::{watcher, RecursiveMode, Watcher}; let account_hash = self.account_hash; - let collection = self.collection.clone(); + let tag_index = self.collection.tag_index.clone(); let lib = self.lib.clone(); let path = self.path.clone(); let revision_uuid = self.revision_uuid.clone(); @@ -748,7 +748,7 @@ impl MailBackend for NotmuchDb { mailboxes.clone(), index.clone(), mailbox_index.clone(), - collection.tag_index.clone(), + tag_index.clone(), account_hash, event_consumer.clone(), new_revision_uuid, @@ -826,7 +826,7 @@ impl MailBackend for NotmuchDb { self.lib.clone(), true, )?; - let collection = self.collection.clone(); + let tag_index = self.collection.clone().tag_index; let index = self.index.clone(); Ok(Box::pin(async move { @@ -914,11 +914,7 @@ impl MailBackend for NotmuchDb { for (f, v) in flags.iter() { if let (Err(tag), true) = (f, v) { let hash = TagHash::from_bytes(tag.as_bytes()); - collection - .tag_index - .write() - .unwrap() - .insert(hash, tag.to_string()); + tag_index.write().unwrap().insert(hash, tag.to_string()); } } diff --git a/melib/src/conf.rs b/melib/src/conf.rs index f2999a95..7cf0a5e5 100644 --- a/melib/src/conf.rs +++ b/melib/src/conf.rs @@ -186,7 +186,7 @@ macro_rules! named_unit_variant { }; } -mod strings { +pub mod strings { named_unit_variant!(ask); } @@ -253,7 +253,7 @@ impl<'de> Deserialize<'de> for ToggleFlag { { #[derive(Deserialize)] #[serde(untagged)] - pub enum InnerToggleFlag { + enum InnerToggleFlag { Bool(bool), #[serde(with = "strings::ask")] Ask, diff --git a/melib/src/email/parser.rs b/melib/src/email/parser.rs index 39fdaedb..8771161f 100644 --- a/melib/src/email/parser.rs +++ b/melib/src/email/parser.rs @@ -2595,25 +2595,26 @@ pub mod address { )) } + ///`no-fold-literal = "[" *dtext "]"` + pub fn no_fold_literal(input: &[u8]) -> IResult<&[u8], Cow<'_, [u8]>> { + let orig_input = input; + let (input, _) = tag("[")(input)?; + let (input, ret) = many0(dtext)(input)?; + let (input, _) = tag("]")(input)?; + Ok((input, Cow::Borrowed(&orig_input[0..ret.len() + 1]))) + } + + ///`id-left = dot-atom-text / obs-id-left` + pub fn id_left(input: &[u8]) -> IResult<&[u8], Cow<'_, [u8]>> { + dot_atom_text(input) + } + ///`id-right = dot-atom-text / no-fold-literal / obs-id-right` + pub fn id_right(input: &[u8]) -> IResult<&[u8], Cow<'_, [u8]>> { + alt((dot_atom_text, no_fold_literal))(input) + } + ///`msg-id = [CFWS] "<" id-left "@" id-right ">" [CFWS]` pub fn msg_id(input: &[u8]) -> IResult<&[u8], MessageID> { - ///`no-fold-literal = "[" *dtext "]"` - pub fn no_fold_literal(input: &[u8]) -> IResult<&[u8], Cow<'_, [u8]>> { - let orig_input = input; - let (input, _) = tag("[")(input)?; - let (input, ret) = many0(dtext)(input)?; - let (input, _) = tag("]")(input)?; - Ok((input, Cow::Borrowed(&orig_input[0..ret.len() + 1]))) - } - - ///`id-left = dot-atom-text / obs-id-left` - pub fn id_left(input: &[u8]) -> IResult<&[u8], Cow<'_, [u8]>> { - dot_atom_text(input) - } - ///`id-right = dot-atom-text / no-fold-literal / obs-id-right` - pub fn id_right(input: &[u8]) -> IResult<&[u8], Cow<'_, [u8]>> { - alt((dot_atom_text, no_fold_literal))(input) - } let (input, _) = opt(cfws)(input)?; let orig_input = input; let (input, _) = tag("<")(input)?; diff --git a/melib/src/gpgme/io.rs b/melib/src/gpgme/io.rs index d716e35b..4316d453 100644 --- a/melib/src/gpgme/io.rs +++ b/melib/src/gpgme/io.rs @@ -30,6 +30,9 @@ struct TagData { io_state: Arc>, } +/// +/// # Safety +/// . pub unsafe extern "C" fn gpgme_register_io_cb( data: *mut ::std::os::raw::c_void, fd: ::std::os::raw::c_int, @@ -66,6 +69,9 @@ pub unsafe extern "C" fn gpgme_register_io_cb( 0 } +/// +/// # Safety +/// . pub unsafe extern "C" fn gpgme_remove_io_cb(tag: *mut ::std::os::raw::c_void) { let tag_data: Arc = Arc::from_raw(tag as *const _); let mut io_state_lck = tag_data.io_state.lock().unwrap(); @@ -75,6 +81,9 @@ pub unsafe extern "C" fn gpgme_remove_io_cb(tag: *mut ::std::os::raw::c_void) { let _ = Arc::into_raw(tag_data); } +/// +/// # Safety +/// . pub unsafe extern "C" fn gpgme_event_io_cb( data: *mut ::std::os::raw::c_void, type_: gpgme_event_io_t, diff --git a/melib/src/gpgme/mod.rs b/melib/src/gpgme/mod.rs index 3e363163..33dbaa25 100644 --- a/melib/src/gpgme/mod.rs +++ b/melib/src/gpgme/mod.rs @@ -72,9 +72,9 @@ macro_rules! c_string_literal { } }}; } -mod bindings; +pub mod bindings; use bindings::*; -mod io; +pub mod io; #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum GpgmeFlag { @@ -804,7 +804,8 @@ impl Context { .chain_err_summary(|| { "libgpgme error: could not perform seek on signature data object" })?; - _ = text; + // disjoint-capture-in-closures + let _ = &text; sig.into_bytes() }) } @@ -1127,7 +1128,8 @@ impl Context { cipher .seek(std::io::SeekFrom::Start(0)) .chain_err_summary(|| "libgpgme error: could not perform seek on plain text")?; - _ = plain; + // disjoint-capture-in-closures + let _ = &plain; cipher.into_bytes() }) } diff --git a/melib/src/text_processing/mod.rs b/melib/src/text_processing/mod.rs index 6abf148b..70cefc23 100644 --- a/melib/src/text_processing/mod.rs +++ b/melib/src/text_processing/mod.rs @@ -22,8 +22,8 @@ pub mod grapheme_clusters; pub mod line_break; pub mod search; -mod tables; -mod types; +pub mod tables; +pub mod types; pub use types::Reflow; pub mod wcwidth; pub use grapheme_clusters::*; diff --git a/melib/src/utils/datetime.rs b/melib/src/utils/datetime.rs index 56832b3e..7dc7cf9d 100644 --- a/melib/src/utils/datetime.rs +++ b/melib/src/utils/datetime.rs @@ -741,7 +741,7 @@ const TIMEZONE_ABBR: &[(&[u8], (i8, i8))] = &[ (b"YEKT", (05, 0)), ]; -mod lib { +pub mod lib { use std::convert::TryFrom; use libc::tm;