use std::path::{Path, PathBuf};
use diesel::{ExpressionMethods, query_dsl, QueryDsl, RunQueryDsl, TextExpressionMethods};
use openpgp_cert_d::{CertD, MergeResult};
use rpgpie::key::Certificate;
use crate::schema::{certs, emails, keyids, subkeys, userids};
#[rustfmt::skip]
mod schema;
mod db;
mod model;
mod util;
const PRELOAD: bool = true;
const DB_FILENAME: &str = "_rpgpie.sqlite";
pub struct Store {
certd: CertD,
db_path: PathBuf,
}
impl Store {
pub fn new() -> openpgp_cert_d::Result<Self> {
Self::with_base_dir(CertD::user_configured_store_path()?)
}
pub fn with_base_dir(base: impl AsRef<Path>) -> openpgp_cert_d::Result<Self> {
let certd = CertD::with_base_dir(base)?;
let mut db_path = certd.base_dir().to_path_buf();
db_path.push(DB_FILENAME);
log::debug!("Store::new with db path {:?}", db_path);
let store = Self { certd, db_path };
store.init(PRELOAD);
Ok(store)
}
pub fn insert(&self, certificate: &Certificate) -> openpgp_cert_d::Result<()> {
let serialized: Vec<u8> = certificate.try_into().expect("fixme");
let fp = certificate.fingerprint();
if let Ok((tag, _)) = self
.certd
.insert(&hex::encode(fp), serialized, false, |this, old| {
if old.is_none() {
Ok(MergeResult::Data(this))
} else {
panic!("merging/updating is not yet implemented");
}
})
{
let mut conn = self.conn();
Self::cache_update(certificate, tag, &mut conn);
} else {
todo!();
}
Ok(())
}
pub fn get_by_primary_fingerprint(
&self,
fingerprint: &str,
) -> openpgp_cert_d::Result<Option<Certificate>> {
let mut conn = self.conn();
self.get_by_primary_with_conn(fingerprint, &mut conn)
}
pub fn search_by_fingerprint(
&self,
fingerprint: &str,
) -> openpgp_cert_d::Result<Vec<Certificate>> {
let mut res = vec![];
let mut conn = self.conn();
if let Ok(Some(primary)) = self.get_by_primary_with_conn(fingerprint, &mut conn) {
res.push(primary);
}
let matches = subkeys::table
.inner_join(certs::table)
.filter(subkeys::fp.eq(fingerprint.to_ascii_lowercase()))
.select(certs::fp)
.load::<String>(&mut conn)
.expect("foo");
matches
.iter()
.flat_map(|fp| self.get_by_primary_with_conn(fp, &mut conn))
.flatten()
.for_each(|c| res.push(c));
Ok(res)
}
pub fn search_by_key_id(&self, key_id: &str) -> openpgp_cert_d::Result<Vec<Certificate>> {
let mut conn = self.conn();
let matches = keyids::table
.inner_join(certs::table)
.filter(keyids::keyid.eq(key_id.to_ascii_lowercase()))
.select(certs::fp)
.load::<String>(&mut conn)
.expect("foo");
let res = matches
.iter()
.flat_map(|fp| self.get_by_primary_with_conn(fp, &mut conn))
.flatten()
.collect();
Ok(res)
}
pub fn search_by_email(&self, email: &str) -> openpgp_cert_d::Result<Vec<Certificate>> {
let mut conn = self.conn();
let matches = emails::table
.inner_join(certs::table)
.filter(emails::email.eq(email))
.select(certs::fp)
.load::<String>(&mut conn)
.expect("foo");
let res = matches
.iter()
.flat_map(|fp| self.get_by_primary_with_conn(fp, &mut conn))
.flatten()
.collect();
Ok(res)
}
pub fn search_exact_user_id(&self, user_id: &str) -> openpgp_cert_d::Result<Vec<Certificate>> {
let mut conn = self.conn();
let matches = userids::table
.inner_join(certs::table)
.filter(userids::userid.eq(user_id))
.select(certs::fp)
.load::<String>(&mut conn)
.expect("foo");
let res = matches
.iter()
.flat_map(|fp| self.get_by_primary_with_conn(fp, &mut conn))
.flatten()
.collect();
Ok(res)
}
pub fn search_like_user_id(&self, like: &str) -> openpgp_cert_d::Result<Vec<Certificate>> {
let mut conn = self.conn();
let matches = query_dsl::methods::GroupByDsl::group_by(
userids::table
.inner_join(certs::table)
.filter(userids::userid.like(like))
.select(certs::fp),
certs::id,
)
.load::<String>(&mut conn)
.expect("foo");
let res = matches
.iter()
.flat_map(|fp| self.get_by_primary_with_conn(fp, &mut conn))
.flatten()
.collect();
Ok(res)
}
}