#[cfg(test)]
mod tests;
use std::fmt;
use std::rc::Rc;
use thiserror::Error;
use crate::svec::SecureVec;
#[derive(Debug, Error)]
pub enum PasswordError {
#[error("a password is needed by the current cipher")]
NoPassword,
#[error("failed to receive the password: {0}")]
PasswordCallback(String),
}
pub type CallbackFn = dyn Fn() -> Result<Vec<u8>, String>;
pub struct PasswordStore {
callback: Option<Rc<CallbackFn>>,
value: Option<SecureVec>,
}
impl PasswordStore {
pub fn new(callback: Option<Rc<CallbackFn>>) -> PasswordStore {
PasswordStore {
callback,
value: None,
}
}
#[cfg(test)]
pub fn with_value(value: &[u8]) -> PasswordStore {
PasswordStore {
callback: None,
value: Some(value.to_vec().into()),
}
}
pub fn value(&mut self) -> Result<&[u8], PasswordError> {
match self.value {
Some(ref v) => Ok(v),
None => {
let callback = self.callback.as_ref().ok_or(PasswordError::NoPassword)?;
let value = callback().map_err(PasswordError::PasswordCallback)?;
Ok(self.value.insert(value.into()))
}
}
}
}
impl fmt::Debug for PasswordStore {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
let callback = match self.callback {
Some(_) => Some(()),
None => None,
};
let value = self.value.as_ref().map(|_| "***");
fmt.debug_struct("PasswordStore")
.field("callback", &callback)
.field("value", &value)
.finish()
}
}