use std::collections::BTreeMap;
use std::collections::BTreeSet;
use std::collections::VecDeque;
use std::str;
use std::sync::RwLock;
use std::sync::{RwLockReadGuard, RwLockWriteGuard};
use sequoia_openpgp as openpgp;
use openpgp::Fingerprint;
use openpgp::Result;
use openpgp::packet::UserID;
use crate::store::StoreError;
use crate::store::UserIDQueryParams;
use super::TRACE;
pub struct UserIDIndex {
inner: RwLock<UserIDIndexInner>,
}
assert_send_and_sync!(UserIDIndex);
pub(crate) struct UserIDIndexInner {
by_userid: BTreeMap<UserID, BTreeSet<Fingerprint>>,
by_email: BTreeMap<String, BTreeSet<Fingerprint>>,
by_email_pending: VecDeque<(UserID, Fingerprint)>,
}
impl UserIDIndexInner {
pub(crate) fn insert(&mut self,
fp: Fingerprint,
userid: UserID,
email: Option<String>)
{
if let Some(email) = email {
self.by_email
.entry(email)
.or_default()
.insert(fp.clone());
} else {
self.by_email_pending
.push_back((userid.clone(), fp.clone()));
}
self.by_userid
.entry(userid)
.or_default()
.insert(fp);
}
}
impl Default for UserIDIndex {
fn default() -> Self {
UserIDIndex {
inner: RwLock::new(UserIDIndexInner {
by_userid: Default::default(),
by_email: Default::default(),
by_email_pending: VecDeque::new(),
}),
}
}
}
impl FromIterator<(Fingerprint, UserID, Option<String>)> for UserIDIndex {
fn from_iter<I: IntoIterator<Item=(Fingerprint, UserID, Option<String>)>>(iter: I) -> Self {
let index = UserIDIndex::default();
let mut inner = index.inner.write().unwrap();
for (fp, userid, email) in iter {
inner.insert(fp, userid, email);
}
drop(inner);
index
}
}
impl UserIDIndex {
pub fn new() -> Self {
Self::default()
}
pub fn insert<I>(&mut self, fpr: &Fingerprint, userids: I)
where I: Iterator<Item=UserID>
{
let mut inner = self.inner.write().unwrap();
for userid in userids {
inner.by_userid
.entry(userid.clone())
.or_default()
.insert(fpr.clone());
inner.by_email_pending
.push_back((userid, fpr.clone()));
}
}
fn execute_pending_insertions(&'_ self) -> RwLockReadGuard<'_, UserIDIndexInner> {
loop {
let inner = self.inner.read().unwrap();
if inner.by_email_pending.is_empty() {
return inner;
}
drop(inner);
let mut inner = self.inner.write().unwrap();
let mut pending = std::mem::take(&mut inner.by_email_pending);
for (userid, fpr) in pending.drain(..) {
if let Ok(Some(email)) = userid.email_normalized() {
inner.by_email
.entry(email)
.or_default()
.insert(fpr.clone());
}
}
drop(inner);
}
}
pub fn select_userid(&self, params: &UserIDQueryParams, pattern: &str)
-> Result<Vec<Fingerprint>>
{
tracer!(TRACE, "UserIDIndex::select_userid");
t!("params: {:?}, pattern: {:?}", params, pattern);
let mut matches = match params {
UserIDQueryParams {
anchor_start: true,
anchor_end: true,
email: false,
ignore_case: false,
} => {
let userid = UserID::from(pattern);
let inner = self.inner.read().unwrap();
inner.by_userid.get(&userid)
.ok_or_else(|| {
StoreError::NoMatches(pattern.into())
})?
.iter()
.cloned()
.collect()
}
UserIDQueryParams {
anchor_start: true,
anchor_end: true,
email: true,
ignore_case: false,
} => {
let inner = self.execute_pending_insertions();
inner.by_email
.get(pattern)
.ok_or_else(|| {
StoreError::NoMatches(pattern.into())
})?
.iter()
.cloned()
.collect()
}
UserIDQueryParams {
anchor_start,
anchor_end,
email,
ignore_case,
} => {
let mut pattern = pattern;
let _pattern;
if *ignore_case {
_pattern = pattern.to_lowercase();
pattern = &_pattern[..];
}
let check = |userid: &str| -> bool {
let mut userid = userid;
let _userid: String;
if *ignore_case {
_userid = userid.to_lowercase();
userid = &_userid[..];
}
t!("Considering if {:?} matches {:?} \
(anchors: {}, {}, ignore case: {})",
pattern, userid, anchor_start, anchor_end,
ignore_case);
if match (*anchor_start, *anchor_end) {
(true, true) => userid == pattern,
(true, false) => userid.starts_with(pattern),
(false, true) => userid.ends_with(pattern),
(false, false) => userid.contains(pattern),
}
{
t!("*** {:?} matches {:?} (anchors: {}, {})",
pattern, userid, *anchor_start, anchor_end);
true
} else {
false
}
};
if *email {
let inner = self.execute_pending_insertions();
inner.by_email
.iter()
.filter_map(|(email, matches)| {
if check(email) {
Some(matches.iter())
} else {
None
}
})
.flatten()
.cloned()
.collect::<Vec<Fingerprint>>()
} else {
let inner = self.inner.read().unwrap();
inner.by_userid
.iter()
.filter_map(|(userid, matches)| {
let userid = String::from_utf8_lossy(userid.value());
if check(&userid) {
Some(matches.iter())
} else {
None
}
})
.flatten()
.cloned()
.collect::<Vec<Fingerprint>>()
}
}
};
if matches.is_empty() {
return Err(StoreError::NoMatches(pattern.into()).into());
}
matches.sort();
matches.dedup();
Ok(matches)
}
pub(crate) fn inner_mut(&'_ self) -> RwLockWriteGuard<'_, UserIDIndexInner> {
self.inner.write().unwrap()
}
}