use crate::error::Result;
#[cfg(feature = "file-backend")]
mod file;
mod memory;
#[cfg(feature = "file-backend")]
pub use file::FileBackend;
pub use memory::MemoryBackend;
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct BackendKey(pub String);
impl BackendKey {
pub fn new(name: impl Into<String>) -> Self {
Self(name.into())
}
pub fn as_str(&self) -> &str {
&self.0
}
}
impl<T: Into<String>> From<T> for BackendKey {
fn from(v: T) -> Self {
Self(v.into())
}
}
impl std::fmt::Display for BackendKey {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(&self.0)
}
}
pub trait KeychainBackend: Send + Sync + 'static {
fn read(&self, key: &BackendKey) -> Result<Vec<u8>>;
fn write(&self, key: &BackendKey, data: &[u8]) -> Result<()>;
fn delete(&self, key: &BackendKey) -> Result<()>;
fn list(&self, prefix: &str) -> Result<Vec<BackendKey>>;
fn exists(&self, key: &BackendKey) -> Result<bool> {
match self.read(key) {
Ok(_) => Ok(true),
Err(crate::error::KeystoreError::Backend(e))
if e.kind() == std::io::ErrorKind::NotFound =>
{
Ok(false)
}
Err(e) => Err(e),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::error::KeystoreError;
use std::io;
#[derive(Default)]
struct ProbeBackend {
fail_kind: Option<io::ErrorKind>,
present: bool,
}
impl KeychainBackend for ProbeBackend {
fn read(&self, _key: &BackendKey) -> Result<Vec<u8>> {
if let Some(kind) = self.fail_kind {
return Err(KeystoreError::from(io::Error::new(kind, "probe")));
}
if self.present {
Ok(vec![1, 2, 3])
} else {
Err(KeystoreError::from(io::Error::new(
io::ErrorKind::NotFound,
"absent",
)))
}
}
fn write(&self, _key: &BackendKey, _data: &[u8]) -> Result<()> {
Ok(())
}
fn delete(&self, _key: &BackendKey) -> Result<()> {
Ok(())
}
fn list(&self, _prefix: &str) -> Result<Vec<BackendKey>> {
Ok(vec![])
}
}
#[test]
fn backend_key_constructors_and_accessors_agree() {
let from_new = BackendKey::new("validator");
let from_into: BackendKey = "validator".into();
let from_string: BackendKey = BackendKey::from(String::from("validator"));
assert_eq!(from_new.as_str(), "validator");
assert_eq!(from_new, from_into);
assert_eq!(from_new, from_string);
assert_eq!(format!("{from_new}"), "validator");
assert_ne!(from_new, BackendKey::new("other"));
}
#[test]
fn default_exists_true_when_present() {
let be = ProbeBackend {
present: true,
..Default::default()
};
assert!(be.exists(&BackendKey::new("k")).unwrap());
}
#[test]
fn default_exists_false_when_not_found() {
let be = ProbeBackend::default(); assert!(!be.exists(&BackendKey::new("k")).unwrap());
}
#[test]
fn default_exists_propagates_other_errors() {
let be = ProbeBackend {
fail_kind: Some(io::ErrorKind::PermissionDenied),
..Default::default()
};
let err = be.exists(&BackendKey::new("k")).unwrap_err();
assert!(matches!(err, KeystoreError::Backend(_)));
}
}