diff --git a/melib/src/backends.rs b/melib/src/backends.rs
index cc913b8e..71ac5186 100644
--- a/melib/src/backends.rs
+++ b/melib/src/backends.rs
@@ -19,6 +19,7 @@
* along with meli. If not, see .
*/
+use smallvec::SmallVec;
#[macro_export]
macro_rules! tag_hash {
($tag:ident) => {{
@@ -328,6 +329,14 @@ pub trait MailBackend: ::std::fmt::Debug + Send + Sync {
) -> Result<()> {
Err(MeliError::new("Unimplemented."))
}
+
+ fn search(
+ &self,
+ _query: crate::search::Query,
+ _mailbox_hash: Option,
+ ) -> Result> {
+ Err(MeliError::new("Unimplemented."))
+ }
}
/// A `BackendOp` manages common operations for the various mail backends. They only live for the
diff --git a/melib/src/backends/imap.rs b/melib/src/backends/imap.rs
index 3b01e3ba..eedb7fa0 100644
--- a/melib/src/backends/imap.rs
+++ b/melib/src/backends/imap.rs
@@ -704,6 +704,136 @@ impl MailBackend for ImapType {
Err(MeliError::new("Unimplemented."))
}
+
+ fn search(
+ &self,
+ query: crate::search::Query,
+ mailbox_hash: Option,
+ ) -> Result> {
+ if mailbox_hash.is_none() {
+ return Err(MeliError::new(
+ "Cannot search without specifying mailbox on IMAP",
+ ));
+ }
+ let mailbox_hash = mailbox_hash.unwrap();
+ fn rec(q: &crate::search::Query, s: &mut String) {
+ use crate::search::{escape_double_quote, Query::*};
+ match q {
+ Subject(t) => {
+ s.push_str(" SUBJECT \"");
+ s.extend(escape_double_quote(t).chars());
+ s.push_str("\"");
+ }
+ From(t) => {
+ s.push_str(" FROM \"");
+ s.extend(escape_double_quote(t).chars());
+ s.push_str("\"");
+ }
+ To(t) => {
+ s.push_str(" TO \"");
+ s.extend(escape_double_quote(t).chars());
+ s.push_str("\"");
+ }
+ Cc(t) => {
+ s.push_str(" CC \"");
+ s.extend(escape_double_quote(t).chars());
+ s.push_str("\"");
+ }
+ Bcc(t) => {
+ s.push_str(" BCC \"");
+ s.extend(escape_double_quote(t).chars());
+ s.push_str("\"");
+ }
+ AllText(t) => {
+ s.push_str(" TEXT \"");
+ s.extend(escape_double_quote(t).chars());
+ s.push_str("\"");
+ }
+ Flags(v) => {
+ for f in v {
+ match f.as_str() {
+ "draft" => {
+ s.push_str(" DRAFT ");
+ }
+ "deleted" => {
+ s.push_str(" DELETED ");
+ }
+ "flagged" => {
+ s.push_str(" FLAGGED ");
+ }
+ "recent" => {
+ s.push_str(" RECENT ");
+ }
+ "seen" | "read" => {
+ s.push_str(" SEEN ");
+ }
+ "unseen" | "unread" => {
+ s.push_str(" UNSEEN ");
+ }
+ "answered" => {
+ s.push_str(" ANSWERED ");
+ }
+ "unanswered" => {
+ s.push_str(" UNANSWERED ");
+ }
+ keyword => {
+ s.push_str(" KEYWORD ");
+ s.extend(keyword.chars());
+ s.push_str(" ");
+ }
+ }
+ }
+ }
+ And(q1, q2) => {
+ rec(q1, s);
+ s.push_str(" ");
+ rec(q2, s);
+ }
+ Or(q1, q2) => {
+ s.push_str(" OR ");
+ rec(q1, s);
+ s.push_str(" ");
+ rec(q2, s);
+ }
+ Not(q) => {
+ s.push_str(" NOT ");
+ rec(q, s);
+ }
+ _ => {}
+ }
+ }
+ let mut query_str = String::new();
+ rec(&query, &mut query_str);
+
+ let mailboxes_lck = self.mailboxes.read()?;
+ let mut response = String::with_capacity(8 * 1024);
+ let mut conn = try_lock(&self.connection)?;
+ conn.send_command(
+ format!("EXAMINE \"{}\"", mailboxes_lck[&mailbox_hash].imap_path()).as_bytes(),
+ )?;
+ conn.read_response(&mut response)?;
+ conn.send_command(format!("UID SEARCH CHARSET UTF-8 {}", query_str).as_bytes())?;
+ conn.read_response(&mut response)?;
+ debug!(&response);
+
+ let mut lines = response.lines();
+ for l in lines.by_ref() {
+ if l.starts_with("* SEARCH") {
+ use std::iter::FromIterator;
+ let uid_index = self.uid_store.uid_index.lock()?;
+ return Ok(SmallVec::from_iter(
+ l["* SEARCH".len()..]
+ .trim()
+ .split_whitespace()
+ .map(usize::from_str)
+ .filter_map(std::result::Result::ok)
+ .filter_map(|uid| uid_index.get(&uid))
+ .map(|env_hash_ref| *env_hash_ref),
+ ));
+ }
+ }
+ Err(MeliError::new(response))
+ }
}
impl ImapType {
@@ -887,41 +1017,6 @@ impl ImapType {
.unwrap_or_default()
}
- pub fn search(
- &self,
- query: String,
- mailbox_hash: MailboxHash,
- ) -> Result> {
- let mailboxes_lck = self.mailboxes.read()?;
- let mut response = String::with_capacity(8 * 1024);
- let mut conn = try_lock(&self.connection)?;
- conn.send_command(
- format!("EXAMINE \"{}\"", mailboxes_lck[&mailbox_hash].imap_path()).as_bytes(),
- )?;
- conn.read_response(&mut response)?;
- conn.send_command(format!("UID SEARCH CHARSET UTF-8 {}", query).as_bytes())?;
- conn.read_response(&mut response)?;
- debug!(&response);
-
- let mut lines = response.lines();
- for l in lines.by_ref() {
- if l.starts_with("* SEARCH") {
- use std::iter::FromIterator;
- let uid_index = self.uid_store.uid_index.lock()?;
- return Ok(SmallVec::from_iter(
- l["* SEARCH".len()..]
- .trim()
- .split_whitespace()
- .map(usize::from_str)
- .filter_map(std::result::Result::ok)
- .filter_map(|uid| uid_index.get(&uid))
- .map(|env_hash_ref| *env_hash_ref),
- ));
- }
- }
- Err(MeliError::new(response))
- }
-
pub fn validate_config(s: &AccountSettings) -> Result<()> {
get_conf_val!(s["server_hostname"])?;
get_conf_val!(s["server_username"])?;
diff --git a/melib/src/lib.rs b/melib/src/lib.rs
index 38000c79..1d3d9076 100644
--- a/melib/src/lib.rs
+++ b/melib/src/lib.rs
@@ -137,6 +137,7 @@ extern crate encoding;
extern crate bitflags;
extern crate fnv;
extern crate uuid;
+pub use smallvec;
pub use crate::backends::{Backends, RefreshEvent, RefreshEventConsumer, SpecialUsageMailbox};
pub use crate::collection::*;
diff --git a/melib/src/search.rs b/melib/src/search.rs
index 12a4f842..c2ca0982 100644
--- a/melib/src/search.rs
+++ b/melib/src/search.rs
@@ -21,14 +21,7 @@
use crate::parsec::*;
use crate::UnixTimestamp;
-use crate::{
- backends::{MailBackend, MailboxHash},
- email::EnvelopeHash,
- thread::{SortField, SortOrder},
- Result,
-};
use std::borrow::Cow;
-use std::sync::{Arc, RwLock};
pub use query_parser::query;
use Query::*;
@@ -229,9 +222,9 @@ pub mod query_parser {
///
/// # Invocation
/// ```
- /// use ui::cache::query;
- /// use ui::cache::Query;
- /// use crate::parsec::Parser;
+ /// use melib::search::query;
+ /// use melib::search::Query;
+ /// use melib::parsec::Parser;
///
/// let input = "test";
/// let query = query().parse(input);
@@ -371,114 +364,6 @@ pub mod query_parser {
}
}
-pub fn query_to_imap(q: &Query) -> String {
- fn rec(q: &Query, s: &mut String) {
- match q {
- Subject(t) => {
- s.push_str(" SUBJECT \"");
- s.extend(escape_double_quote(t).chars());
- s.push_str("\"");
- }
- From(t) => {
- s.push_str(" FROM \"");
- s.extend(escape_double_quote(t).chars());
- s.push_str("\"");
- }
- To(t) => {
- s.push_str(" TO \"");
- s.extend(escape_double_quote(t).chars());
- s.push_str("\"");
- }
- Cc(t) => {
- s.push_str(" CC \"");
- s.extend(escape_double_quote(t).chars());
- s.push_str("\"");
- }
- Bcc(t) => {
- s.push_str(" BCC \"");
- s.extend(escape_double_quote(t).chars());
- s.push_str("\"");
- }
- AllText(t) => {
- s.push_str(" TEXT \"");
- s.extend(escape_double_quote(t).chars());
- s.push_str("\"");
- }
- Flags(v) => {
- for f in v {
- match f.as_str() {
- "draft" => {
- s.push_str(" DRAFT ");
- }
- "deleted" => {
- s.push_str(" DELETED ");
- }
- "flagged" => {
- s.push_str(" FLAGGED ");
- }
- "recent" => {
- s.push_str(" RECENT ");
- }
- "seen" | "read" => {
- s.push_str(" SEEN ");
- }
- "unseen" | "unread" => {
- s.push_str(" UNSEEN ");
- }
- "answered" => {
- s.push_str(" ANSWERED ");
- }
- "unanswered" => {
- s.push_str(" UNANSWERED ");
- }
- keyword => {
- s.push_str(" KEYWORD ");
- s.extend(keyword.chars());
- s.push_str(" ");
- }
- }
- }
- }
- And(q1, q2) => {
- rec(q1, s);
- s.push_str(" ");
- rec(q2, s);
- }
- Or(q1, q2) => {
- s.push_str(" OR ");
- rec(q1, s);
- s.push_str(" ");
- rec(q2, s);
- }
- Not(q) => {
- s.push_str(" NOT ");
- rec(q, s);
- }
- _ => {}
- }
- }
- let mut ret = String::new();
- rec(q, &mut ret);
- ret
-}
-
-pub fn imap_search(
- term: &str,
- (_sort_field, _sort_order): (SortField, SortOrder),
- mailbox_hash: MailboxHash,
- backend: &Arc>>,
-) -> Result> {
- let query = query().parse(term)?.1;
- let backend_lck = backend.read().unwrap();
-
- let b = (*backend_lck).as_any();
- if let Some(imap_backend) = b.downcast_ref::() {
- imap_backend.search(query_to_imap(&query), mailbox_hash)
- } else {
- panic!("Could not downcast ImapType backend. BUG");
- }
-}
-
#[inline(always)]
pub fn escape_double_quote(w: &str) -> Cow {
if w.contains('"') {
diff --git a/src/conf/accounts.rs b/src/conf/accounts.rs
index cd91d9e3..9475af5d 100644
--- a/src/conf/accounts.rs
+++ b/src/conf/accounts.rs
@@ -1133,7 +1133,13 @@ impl Account {
mailbox_hash: MailboxHash,
) -> Result> {
if self.settings.account().format() == "imap" {
- return melib::search::imap_search(search_term, sort, mailbox_hash, &self.backend);
+ use melib::parsec::Parser;
+ let query = melib::search::query().parse(search_term)?.1;
+ return self
+ .backend
+ .read()
+ .unwrap()
+ .search(query, Some(mailbox_hash));
}
#[cfg(feature = "notmuch")]