use std::collections::HashMap;
use parking_lot::Mutex;
use crate::backend::{BackendKey, KeychainBackend};
use crate::error::{KeystoreError, Result};
#[derive(Default)]
pub struct MemoryBackend {
inner: Mutex<HashMap<BackendKey, Vec<u8>>>,
}
impl MemoryBackend {
pub fn new() -> Self {
Self::default()
}
}
impl KeychainBackend for MemoryBackend {
fn read(&self, key: &BackendKey) -> Result<Vec<u8>> {
self.inner.lock().get(key).cloned().ok_or_else(|| {
KeystoreError::from(std::io::Error::new(
std::io::ErrorKind::NotFound,
format!("key not found: {key}"),
))
})
}
fn write(&self, key: &BackendKey, data: &[u8]) -> Result<()> {
self.inner.lock().insert(key.clone(), data.to_vec());
Ok(())
}
fn delete(&self, key: &BackendKey) -> Result<()> {
self.inner.lock().remove(key);
Ok(())
}
fn list(&self, prefix: &str) -> Result<Vec<BackendKey>> {
Ok(self
.inner
.lock()
.keys()
.filter(|k| k.as_str().starts_with(prefix))
.cloned()
.collect())
}
fn exists(&self, key: &BackendKey) -> Result<bool> {
Ok(self.inner.lock().contains_key(key))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn roundtrip() {
let be = MemoryBackend::new();
let k = BackendKey::new("x");
be.write(&k, b"data").unwrap();
assert_eq!(be.read(&k).unwrap(), b"data");
assert!(be.exists(&k).unwrap());
be.delete(&k).unwrap();
assert!(!be.exists(&k).unwrap());
}
#[test]
fn read_missing_key_is_not_found() {
let be = MemoryBackend::new();
let err = be.read(&BackendKey::new("absent")).unwrap_err();
match err {
KeystoreError::Backend(io) => {
assert_eq!(io.kind(), std::io::ErrorKind::NotFound);
}
other => panic!("expected Backend(NotFound), got {other:?}"),
}
}
#[test]
fn write_overwrites_in_place() {
let be = MemoryBackend::new();
let k = BackendKey::new("k");
be.write(&k, b"first").unwrap();
be.write(&k, b"second").unwrap();
assert_eq!(be.read(&k).unwrap(), b"second");
}
#[test]
fn list_filters_by_prefix() {
let be = MemoryBackend::new();
be.write(&BackendKey::new("validator/a"), b"1").unwrap();
be.write(&BackendKey::new("validator/b"), b"2").unwrap();
be.write(&BackendKey::new("wallet/c"), b"3").unwrap();
let mut matched: Vec<String> = be
.list("validator/")
.unwrap()
.into_iter()
.map(|k| k.as_str().to_string())
.collect();
matched.sort();
assert_eq!(matched, vec!["validator/a", "validator/b"]);
assert_eq!(be.list("").unwrap().len(), 3);
assert!(be.list("none/").unwrap().is_empty());
}
#[test]
fn default_is_empty() {
let be = MemoryBackend::default();
assert!(be.list("").unwrap().is_empty());
assert!(!be.exists(&BackendKey::new("anything")).unwrap());
}
}