mirror of
https://github.com/pimalaya/himalaya.git
synced 2026-06-16 20:57:53 +08:00
replace sqlite by sled for id mapping storing
This commit is contained in:
+5
-17
@@ -619,32 +619,20 @@ impl Backend {
|
||||
match backend_kind {
|
||||
#[cfg(feature = "maildir")]
|
||||
Some(BackendKind::Maildir) => {
|
||||
if let Some(mdir_config) = &self.toml_account_config.maildir {
|
||||
id_mapper = IdMapper::new(
|
||||
&self.backend.account_config,
|
||||
folder,
|
||||
mdir_config.root_dir.clone(),
|
||||
)?;
|
||||
if let Some(_) = &self.toml_account_config.maildir {
|
||||
id_mapper = IdMapper::new(&self.backend.account_config, folder)?;
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "account-sync")]
|
||||
Some(BackendKind::MaildirForSync) => {
|
||||
id_mapper = IdMapper::new(
|
||||
&self.backend.account_config,
|
||||
folder,
|
||||
self.backend.account_config.get_sync_dir()?,
|
||||
)?;
|
||||
id_mapper = IdMapper::new(&self.backend.account_config, folder)?;
|
||||
}
|
||||
|
||||
#[cfg(feature = "notmuch")]
|
||||
Some(BackendKind::Notmuch) => {
|
||||
if let Some(notmuch_config) = &self.toml_account_config.notmuch {
|
||||
id_mapper = IdMapper::new(
|
||||
&self.backend.account_config,
|
||||
folder,
|
||||
notmuch_config.get_maildir_path()?,
|
||||
)?;
|
||||
if let Some(_) = &self.toml_account_config.notmuch {
|
||||
id_mapper = IdMapper::new(&self.backend.account_config, folder)?;
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
|
||||
Vendored
+68
-92
@@ -2,61 +2,34 @@ pub mod arg;
|
||||
pub mod args;
|
||||
|
||||
use anyhow::{anyhow, Context, Result};
|
||||
use dirs::data_dir;
|
||||
use email::account::config::AccountConfig;
|
||||
use log::{debug, trace};
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
const ID_MAPPER_DB_FILE_NAME: &str = ".id-mapper.sqlite";
|
||||
use log::debug;
|
||||
use sled::{Config, Db};
|
||||
use std::collections::HashSet;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum IdMapper {
|
||||
Dummy,
|
||||
Mapper(String, rusqlite::Connection),
|
||||
Mapper(Db),
|
||||
}
|
||||
|
||||
impl IdMapper {
|
||||
pub fn find_closest_db_path(dir: impl AsRef<Path>) -> PathBuf {
|
||||
let mut db_path = dir.as_ref().join(ID_MAPPER_DB_FILE_NAME);
|
||||
let mut db_parent_dir = dir.as_ref().parent();
|
||||
pub fn new(account_config: &AccountConfig, folder: &str) -> Result<Self> {
|
||||
let digest = md5::compute(account_config.name.clone() + folder);
|
||||
let db_path = data_dir()
|
||||
.ok_or(anyhow!("cannot get XDG data directory"))?
|
||||
.join("himalaya")
|
||||
.join(".id-mappers")
|
||||
.join(format!("{digest:x}"));
|
||||
|
||||
while !db_path.is_file() {
|
||||
match db_parent_dir {
|
||||
Some(dir) => {
|
||||
db_path = dir.join(ID_MAPPER_DB_FILE_NAME);
|
||||
db_parent_dir = dir.parent();
|
||||
}
|
||||
None => {
|
||||
db_path = dir.as_ref().join(ID_MAPPER_DB_FILE_NAME);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
db_path
|
||||
}
|
||||
|
||||
pub fn new(account_config: &AccountConfig, folder: &str, db_path: PathBuf) -> Result<Self> {
|
||||
let folder = account_config.get_folder_alias(folder);
|
||||
let digest = md5::compute(account_config.name.clone() + &folder);
|
||||
let table = format!("id_mapper_{digest:x}");
|
||||
debug!("creating id mapper table {table} at {db_path:?}…");
|
||||
|
||||
let db_path = Self::find_closest_db_path(db_path);
|
||||
let conn = rusqlite::Connection::open(&db_path)
|
||||
let conn = Config::new()
|
||||
.path(&db_path)
|
||||
.idgen_persist_interval(1)
|
||||
.open()
|
||||
.with_context(|| format!("cannot open id mapper database at {db_path:?}"))?;
|
||||
|
||||
let query = format!(
|
||||
"CREATE TABLE IF NOT EXISTS {table} (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
internal_id TEXT UNIQUE
|
||||
)",
|
||||
);
|
||||
trace!("create table query: {query:#?}");
|
||||
|
||||
conn.execute(&query, [])
|
||||
.context("cannot create id mapper table")?;
|
||||
|
||||
Ok(Self::Mapper(table, conn))
|
||||
Ok(Self::Mapper(conn))
|
||||
}
|
||||
|
||||
pub fn create_alias<I>(&self, id: I) -> Result<String>
|
||||
@@ -66,18 +39,18 @@ impl IdMapper {
|
||||
let id = id.as_ref();
|
||||
match self {
|
||||
Self::Dummy => Ok(id.to_owned()),
|
||||
Self::Mapper(table, conn) => {
|
||||
Self::Mapper(conn) => {
|
||||
debug!("creating alias for id {id}…");
|
||||
|
||||
let query = format!("INSERT OR IGNORE INTO {} (internal_id) VALUES (?)", table);
|
||||
trace!("insert query: {query:#?}");
|
||||
|
||||
conn.execute(&query, [id])
|
||||
.with_context(|| format!("cannot create id alias for id {id}"))?;
|
||||
|
||||
let alias = conn.last_insert_rowid().to_string();
|
||||
let alias = conn
|
||||
.generate_id()
|
||||
.with_context(|| format!("cannot create alias for id {id}"))?
|
||||
.to_string();
|
||||
debug!("created alias {alias} for id {id}");
|
||||
|
||||
conn.insert(&id, alias.as_bytes())
|
||||
.with_context(|| format!("cannot insert alias {alias} for id {id}"))?;
|
||||
|
||||
Ok(alias)
|
||||
}
|
||||
}
|
||||
@@ -90,22 +63,16 @@ impl IdMapper {
|
||||
let id = id.as_ref();
|
||||
match self {
|
||||
Self::Dummy => Ok(id.to_owned()),
|
||||
Self::Mapper(table, conn) => {
|
||||
Self::Mapper(conn) => {
|
||||
debug!("getting alias for id {id}…");
|
||||
|
||||
let query = format!("SELECT id FROM {} WHERE internal_id = ?", table);
|
||||
trace!("select query: {query:#?}");
|
||||
let alias = conn
|
||||
.get(id)
|
||||
.with_context(|| format!("cannot get alias for id {id}"))?;
|
||||
|
||||
let mut stmt = conn
|
||||
.prepare(&query)
|
||||
.with_context(|| format!("cannot get alias for id {id}"))?;
|
||||
let aliases: Vec<i64> = stmt
|
||||
.query_map([id], |row| row.get(0))
|
||||
.with_context(|| format!("cannot get alias for id {id}"))?
|
||||
.collect::<rusqlite::Result<_>>()
|
||||
.with_context(|| format!("cannot get alias for id {id}"))?;
|
||||
let alias = match aliases.first() {
|
||||
let alias = match alias {
|
||||
Some(alias) => {
|
||||
let alias = String::from_utf8_lossy(alias.as_ref());
|
||||
debug!("found alias {alias} for id {id}");
|
||||
alias.to_string()
|
||||
}
|
||||
@@ -125,30 +92,24 @@ impl IdMapper {
|
||||
A: ToString,
|
||||
{
|
||||
let alias = alias.to_string();
|
||||
let alias = alias
|
||||
.parse::<i64>()
|
||||
.context(format!("cannot parse id mapper alias {alias}"))?;
|
||||
|
||||
match self {
|
||||
Self::Dummy => Ok(alias.to_string()),
|
||||
Self::Mapper(table, conn) => {
|
||||
Self::Mapper(conn) => {
|
||||
debug!("getting id from alias {alias}…");
|
||||
|
||||
let query = format!("SELECT internal_id FROM {} WHERE id = ?", table);
|
||||
trace!("select query: {query:#?}");
|
||||
|
||||
let mut stmt = conn
|
||||
.prepare(&query)
|
||||
.with_context(|| format!("cannot get id from alias {alias}"))?;
|
||||
let ids: Vec<String> = stmt
|
||||
.query_map([alias], |row| row.get(0))
|
||||
.with_context(|| format!("cannot get id from alias {alias}"))?
|
||||
.collect::<rusqlite::Result<_>>()
|
||||
.with_context(|| format!("cannot get id from alias {alias}"))?;
|
||||
let id = ids
|
||||
.first()
|
||||
.ok_or_else(|| anyhow!("cannot get id from alias {alias}"))?
|
||||
.to_owned();
|
||||
let id = conn
|
||||
.iter()
|
||||
.flat_map(|entry| entry)
|
||||
.find_map(|(entry_id, entry_alias)| {
|
||||
if entry_alias.as_ref() == alias.as_bytes() {
|
||||
let entry_id = String::from_utf8_lossy(entry_id.as_ref());
|
||||
Some(entry_id.to_string())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.ok_or_else(|| anyhow!("cannot get id from alias {alias}"))?;
|
||||
debug!("found id {id} from alias {alias}");
|
||||
|
||||
Ok(id)
|
||||
@@ -156,14 +117,29 @@ impl IdMapper {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_ids<A, I>(&self, aliases: I) -> Result<Vec<String>>
|
||||
where
|
||||
A: ToString,
|
||||
I: IntoIterator<Item = A>,
|
||||
{
|
||||
aliases
|
||||
.into_iter()
|
||||
.map(|alias| self.get_id(alias))
|
||||
.collect()
|
||||
pub fn get_ids(&self, aliases: impl IntoIterator<Item = impl ToString>) -> Result<Vec<String>> {
|
||||
let aliases: Vec<String> = aliases.into_iter().map(|alias| alias.to_string()).collect();
|
||||
|
||||
match self {
|
||||
Self::Dummy => Ok(aliases),
|
||||
Self::Mapper(conn) => {
|
||||
let aliases: HashSet<&str> = aliases.iter().map(|alias| alias.as_str()).collect();
|
||||
let ids: Vec<String> = conn
|
||||
.iter()
|
||||
.flat_map(|entry| entry)
|
||||
.filter_map(|(entry_id, entry_alias)| {
|
||||
let alias = String::from_utf8_lossy(entry_alias.as_ref());
|
||||
if aliases.contains(alias.as_ref()) {
|
||||
let entry_id = String::from_utf8_lossy(entry_id.as_ref());
|
||||
Some(entry_id.to_string())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
|
||||
Ok(ids)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user