use commit::Commit;
use errors::*;
use secret::Secret;
use crypto_mac::Mac;
use serde::Serialize;
use std::cmp::Eq;
use std::collections::hash_map::{Entry, HashMap};
use std::hash::Hash;
use std::ops::BitXorAssign;
pub struct Reveal<T, I, M>
where
M: Mac,
{
commits: HashMap<I, Commit<T, M>>,
secrets: HashMap<I, Secret<T, M>>,
}
impl<T, I, M> Reveal<T, I, M>
where
M: Mac,
I: Eq + Hash,
{
pub(crate) fn new(commits: HashMap<I, Commit<T, M>>) -> Self {
Self {
commits,
secrets: HashMap::new(),
}
}
pub fn insert(&mut self, id: I, secret: Secret<T, M>) -> Result<()> {
if !self.commits.contains_key(&id) {
bail!(ErrorKind::NotPresent);
}
match self.secrets.entry(id) {
Entry::Occupied(_) => bail!(ErrorKind::AlreadyInserted),
Entry::Vacant(v) => {
v.insert(secret);
}
}
Ok(())
}
}
impl<T, I, M> Reveal<T, I, M>
where
M: Mac,
I: Eq + Hash,
T: Serialize + BitXorAssign,
{
pub fn get(mut self) -> RevealResult<T, I> {
let mut failed = vec![];
let mut result = None;
for (id, commit) in self.commits.into_iter() {
let secret = match self.secrets.remove(&id) {
Some(x) => x,
None => {
failed.push((RevealErrorKind::MissingSecret, id));
continue;
}
};
let value = match secret.validate(commit) {
Ok(v) => v,
Err(_) => {
failed.push((RevealErrorKind::ValidationFailed, id));
continue;
}
};
if let Some(ref mut v) = result {
*v ^= value;
} else {
result = Some(value);
}
}
if failed.is_empty() {
if let Some(result) = result {
Ok(result)
} else {
panic!("no errors and no result? This is a bug!");
}
} else {
Err(RevealError::new(failed))
}
}
}