mirror of
https://git.meli.delivery/meli/meli
synced 2024-11-17 03:26:20 +00:00
Cache and Sqlite3 cleanups
This commit is contained in:
parent
e396b2f72b
commit
27edd96493
135
ui/src/cache.rs
135
ui/src/cache.rs
@ -19,13 +19,18 @@
|
||||
* along with meli. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/*
|
||||
use melib::backends::{FolderHash, MailBackend};
|
||||
use melib::mailbox::*;
|
||||
use melib::thread::{ThreadHash, ThreadNode};
|
||||
use std::sync::RwLock;
|
||||
*/
|
||||
use melib::email::{Flag, UnixTimestamp};
|
||||
use melib::parsec::*;
|
||||
use melib::{
|
||||
backends::{FolderHash, MailBackend},
|
||||
email::EnvelopeHash,
|
||||
thread::{SortField, SortOrder},
|
||||
Result, StackVec,
|
||||
};
|
||||
use std::sync::{Arc, RwLock};
|
||||
|
||||
pub use query_parser::query;
|
||||
use Query::*;
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum Query {
|
||||
@ -52,103 +57,11 @@ pub enum Query {
|
||||
Not(Box<Query>),
|
||||
}
|
||||
|
||||
/*
|
||||
enum CacheType {
|
||||
Sqlite3,
|
||||
}
|
||||
|
||||
pub struct Cache {
|
||||
collection: Collection,
|
||||
kind: CacheType,
|
||||
backend: Box<dyn MailBackend>,
|
||||
}
|
||||
|
||||
impl Cache {
|
||||
pub fn build_index(&mut self) {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
pub fn new(backend: Box<dyn MailBackend>) -> Self {
|
||||
unimplemented!()
|
||||
}
|
||||
pub fn get_env(&self, h: &EnvelopeHash) -> &Envelope {
|
||||
&self.collection[h]
|
||||
}
|
||||
pub fn get_env_mut(&mut self, h: &EnvelopeHash) -> &mut Envelope {
|
||||
self.collection.entry(*h).or_default()
|
||||
}
|
||||
pub fn contains_key(&self, h: EnvelopeHash) -> bool {
|
||||
self.collection.contains_key(&h)
|
||||
}
|
||||
/*
|
||||
pub fn operation(&self, h: EnvelopeHash) -> Box<dyn BackendOp> {
|
||||
//let operation = self.backend.operation(h, m.folder.hash())
|
||||
unimplemented!()
|
||||
unreachable!()
|
||||
}
|
||||
*/
|
||||
pub fn thread_to_mail_mut(&mut self, h: ThreadHash, f: FolderHash) -> &mut Envelope {
|
||||
self.collection
|
||||
.envelopes
|
||||
.entry(self.collection.threads[&f].thread_to_mail(h))
|
||||
.or_default()
|
||||
}
|
||||
pub fn thread_to_mail(&self, h: ThreadHash, f: FolderHash) -> &Envelope {
|
||||
&self.collection.envelopes[&self.collection.threads[&f].thread_to_mail(h)]
|
||||
}
|
||||
pub fn threaded_mail(&self, h: ThreadHash, f: FolderHash) -> EnvelopeHash {
|
||||
self.collection.threads[&f].thread_to_mail(h)
|
||||
}
|
||||
pub fn mail_and_thread(
|
||||
&mut self,
|
||||
i: EnvelopeHash,
|
||||
f: FolderHash,
|
||||
) -> (&mut Envelope, &ThreadNode) {
|
||||
let thread;
|
||||
{
|
||||
let x = &mut self.collection.envelopes.entry(i).or_default();
|
||||
thread = &self.collection.threads[&f][&x.thread()];
|
||||
}
|
||||
(self.collection.envelopes.entry(i).or_default(), thread)
|
||||
}
|
||||
pub fn thread(&self, h: ThreadHash, f: FolderHash) -> &ThreadNode {
|
||||
&self.collection.threads[&f].thread_nodes()[&h]
|
||||
}
|
||||
}
|
||||
*/
|
||||
impl std::ops::Not for Query {
|
||||
type Output = Query;
|
||||
fn not(self) -> Query {
|
||||
match self {
|
||||
Query::Not(q) => *q,
|
||||
q => Query::Not(Box::new(q)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::BitAnd for Query {
|
||||
type Output = Self;
|
||||
|
||||
fn bitand(self, rhs: Self) -> Self {
|
||||
Query::And(Box::new(self), Box::new(rhs))
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::BitOr for Query {
|
||||
type Output = Self;
|
||||
|
||||
fn bitor(self, rhs: Self) -> Self {
|
||||
Query::Or(Box::new(self), Box::new(rhs))
|
||||
}
|
||||
}
|
||||
|
||||
pub use query_parser::query;
|
||||
|
||||
pub mod query_parser {
|
||||
use super::Query::{self, *};
|
||||
use melib::parsec::*;
|
||||
|
||||
pub fn subject<'a>() -> impl Parser<'a, Query> {
|
||||
fn subject<'a>() -> impl Parser<'a, Query> {
|
||||
prefix(
|
||||
whitespace_wrap(match_literal("subject:")),
|
||||
whitespace_wrap(literal()),
|
||||
@ -156,7 +69,7 @@ pub mod query_parser {
|
||||
.map(|term| Query::Subject(term))
|
||||
}
|
||||
|
||||
pub fn from<'a>() -> impl Parser<'a, Query> {
|
||||
fn from<'a>() -> impl Parser<'a, Query> {
|
||||
prefix(
|
||||
whitespace_wrap(match_literal("from:")),
|
||||
whitespace_wrap(literal()),
|
||||
@ -164,7 +77,7 @@ pub mod query_parser {
|
||||
.map(|term| Query::From(term))
|
||||
}
|
||||
|
||||
pub fn or<'a>() -> impl Parser<'a, Query> {
|
||||
fn or<'a>() -> impl Parser<'a, Query> {
|
||||
move |input| {
|
||||
whitespace_wrap(match_literal_anycase("or"))
|
||||
.parse(input)
|
||||
@ -172,7 +85,7 @@ pub mod query_parser {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn not<'a>() -> impl Parser<'a, Query> {
|
||||
fn not<'a>() -> impl Parser<'a, Query> {
|
||||
move |input| {
|
||||
whitespace_wrap(either(
|
||||
match_literal_anycase("not"),
|
||||
@ -183,7 +96,7 @@ pub mod query_parser {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn and<'a>() -> impl Parser<'a, Query> {
|
||||
fn and<'a>() -> impl Parser<'a, Query> {
|
||||
move |input| {
|
||||
whitespace_wrap(match_literal_anycase("and"))
|
||||
.parse(input)
|
||||
@ -191,11 +104,11 @@ pub mod query_parser {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn literal<'a>() -> impl Parser<'a, String> {
|
||||
fn literal<'a>() -> impl Parser<'a, String> {
|
||||
move |input| either(quoted_string(), string()).parse(input)
|
||||
}
|
||||
|
||||
pub fn parentheses_query<'a>() -> impl Parser<'a, Query> {
|
||||
fn parentheses_query<'a>() -> impl Parser<'a, Query> {
|
||||
move |input| {
|
||||
delimited(
|
||||
whitespace_wrap(match_literal("(")),
|
||||
@ -206,6 +119,18 @@ pub mod query_parser {
|
||||
}
|
||||
}
|
||||
|
||||
/// Parser from `String` to `Query`.
|
||||
///
|
||||
/// # Invocation
|
||||
/// ```
|
||||
/// use ui::cache::query;
|
||||
/// use ui::cache::Query;
|
||||
/// use melib::parsec::Parser;
|
||||
///
|
||||
/// let input = "test";
|
||||
/// let query = query().parse(input);
|
||||
/// assert_eq!(Ok(("", Query::AllText("test".to_string()))), query);
|
||||
/// ```
|
||||
pub fn query<'a>() -> impl Parser<'a, Query> {
|
||||
move |input| {
|
||||
let (rest, query_a): (&'a str, Query) = if let Ok(q) = parentheses_query().parse(input)
|
||||
|
@ -43,29 +43,29 @@ fn escape_double_quote(w: &str) -> Cow<str> {
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn fts5_bareword(w: &str) -> Cow<str> {
|
||||
if w == "AND" || w == "OR" || w == "NOT" {
|
||||
Cow::from(w)
|
||||
} else {
|
||||
if !w.is_ascii() {
|
||||
Cow::from(format!("\"{}\"", escape_double_quote(w)))
|
||||
} else {
|
||||
for &b in w.as_bytes() {
|
||||
if !(b > 0x2f && b < 0x3a)
|
||||
|| !(b > 0x40 && b < 0x5b)
|
||||
|| !(b > 0x60 && b < 0x7b)
|
||||
|| b != 0x60
|
||||
|| b != 26
|
||||
{
|
||||
return Cow::from(format!("\"{}\"", escape_double_quote(w)));
|
||||
}
|
||||
}
|
||||
Cow::from(w)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//#[inline(always)]
|
||||
//fn fts5_bareword(w: &str) -> Cow<str> {
|
||||
// if w == "AND" || w == "OR" || w == "NOT" {
|
||||
// Cow::from(w)
|
||||
// } else {
|
||||
// if !w.is_ascii() {
|
||||
// Cow::from(format!("\"{}\"", escape_double_quote(w)))
|
||||
// } else {
|
||||
// for &b in w.as_bytes() {
|
||||
// if !(b > 0x2f && b < 0x3a)
|
||||
// || !(b > 0x40 && b < 0x5b)
|
||||
// || !(b > 0x60 && b < 0x7b)
|
||||
// || b != 0x60
|
||||
// || b != 26
|
||||
// {
|
||||
// return Cow::from(format!("\"{}\"", escape_double_quote(w)));
|
||||
// }
|
||||
// }
|
||||
// Cow::from(w)
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
//
|
||||
pub fn open_db() -> Result<Connection> {
|
||||
let data_dir =
|
||||
xdg::BaseDirectories::with_prefix("meli").map_err(|e| MeliError::new(e.to_string()))?;
|
||||
@ -302,11 +302,6 @@ pub fn search(
|
||||
SortOrder::Desc => "DESC",
|
||||
};
|
||||
|
||||
/*
|
||||
debug!("SELECT hash FROM envelopes INNER JOIN fts ON fts.rowid = envelopes.id WHERE fts MATCH ? ORDER BY {} {};", sort_field, sort_order);
|
||||
let mut stmt = conn.prepare(
|
||||
format!("SELECT hash FROM envelopes INNER JOIN fts ON fts.rowid = envelopes.id WHERE fts MATCH ? ORDER BY {} {};", sort_field, sort_order).as_str())
|
||||
*/
|
||||
let mut stmt = conn
|
||||
.prepare(
|
||||
debug!(format!(
|
||||
@ -334,69 +329,43 @@ pub fn search(
|
||||
results
|
||||
}
|
||||
|
||||
pub fn from(term: &str) -> Result<StackVec<EnvelopeHash>> {
|
||||
let conn = open_db()?;
|
||||
let mut stmt = conn
|
||||
.prepare("SELECT hash FROM envelopes WHERE _from LIKE ?;")
|
||||
.map_err(|e| MeliError::new(e.to_string()))?;
|
||||
|
||||
let results = stmt
|
||||
.query_map(&[term.trim()], |row| Ok(row.get(0)?))
|
||||
.map_err(|e| MeliError::new(e.to_string()))?
|
||||
.map(|r: std::result::Result<Vec<u8>, rusqlite::Error>| {
|
||||
Ok(u64::from_be_bytes(
|
||||
r.map_err(|e| MeliError::new(e.to_string()))?
|
||||
.as_slice()
|
||||
.try_into()
|
||||
.map_err(|e: std::array::TryFromSliceError| MeliError::new(e.to_string()))?,
|
||||
))
|
||||
})
|
||||
.collect::<Result<StackVec<EnvelopeHash>>>();
|
||||
results
|
||||
}
|
||||
|
||||
/// Translates a `Query` to an Sqlite3 expression in a `String`.
|
||||
pub fn query_to_sql(q: &Query) -> String {
|
||||
fn rec(q: &Query, s: &mut String) {
|
||||
match q {
|
||||
Subject(t) => {
|
||||
s.push_str(" subject LIKE \"%");
|
||||
s.push_str("subject LIKE \"%");
|
||||
s.extend(escape_double_quote(t).chars());
|
||||
s.push_str("%\"");
|
||||
s.push_str("%\" ");
|
||||
}
|
||||
From(t) => {
|
||||
s.push_str(" _from LIKE \"%");
|
||||
s.push_str("_from LIKE \"%");
|
||||
s.extend(escape_double_quote(t).chars());
|
||||
s.push_str("%\"");
|
||||
s.push_str("%\" ");
|
||||
}
|
||||
AllText(t) => {
|
||||
s.push_str(" body_text LIKE \"%");
|
||||
s.push_str("body_text LIKE \"%");
|
||||
s.extend(escape_double_quote(t).chars());
|
||||
s.push_str("%\"");
|
||||
s.push_str("%\" ");
|
||||
}
|
||||
And(q1, q2) => {
|
||||
s.push_str(" (");
|
||||
s.push_str("(");
|
||||
rec(q1, s);
|
||||
s.push_str(") ");
|
||||
|
||||
s.push_str(" AND ");
|
||||
s.push_str(" (");
|
||||
s.push_str(") AND (");
|
||||
rec(q2, s);
|
||||
s.push_str(") ");
|
||||
}
|
||||
Or(q1, q2) => {
|
||||
s.push_str(" (");
|
||||
s.push_str("(");
|
||||
rec(q1, s);
|
||||
s.push_str(") ");
|
||||
s.push_str(" OR ");
|
||||
s.push_str(" (");
|
||||
s.push_str(") OR (");
|
||||
rec(q2, s);
|
||||
s.push_str(") ");
|
||||
}
|
||||
Not(q) => {
|
||||
s.push_str(" NOT ");
|
||||
s.push_str("(");
|
||||
s.push_str("NOT (");
|
||||
rec(q, s);
|
||||
s.push_str(")");
|
||||
s.push_str(") ");
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
@ -404,18 +373,16 @@ pub fn query_to_sql(q: &Query) -> String {
|
||||
let mut ret = String::new();
|
||||
rec(q, &mut ret);
|
||||
ret
|
||||
|
||||
//"SELECT hash FROM envelopes INNER JOIN fts ON fts.rowid = envelopes.id WHERE fts MATCH ? ORDER BY {} {};"
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_query_to_sql() {
|
||||
assert_eq!(
|
||||
" subject LIKE \"%test%\" AND body_text LIKE \"%i%\"",
|
||||
"(subject LIKE \"%test%\" ) AND (body_text LIKE \"%i%\" ) ",
|
||||
&query_to_sql(&query().parse_complete("subject: test and i").unwrap().1)
|
||||
);
|
||||
assert_eq!(
|
||||
" subject LIKE \"%github%\" OR ( _from LIKE \"%epilys%\" AND ( subject LIKE \"%lib%\" OR subject LIKE \"%meli%\") ) ",
|
||||
"(subject LIKE \"%github%\" ) OR ((_from LIKE \"%epilys%\" ) AND ((subject LIKE \"%lib%\" ) OR (subject LIKE \"%meli%\" ) ) ) ",
|
||||
&query_to_sql(
|
||||
&query()
|
||||
.parse_complete(
|
||||
|
Loading…
Reference in New Issue
Block a user