use std::collections::HashMap;
use crate::simulation::fault::{FaultConfig, FaultInjector};
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum SimStorageError {
SimulatedCrash {
at_byte_offset: u64,
},
}
impl std::fmt::Display for SimStorageError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::SimulatedCrash { at_byte_offset } => {
write!(f, "simulated crash at byte offset {at_byte_offset}")
}
}
}
}
impl std::error::Error for SimStorageError {}
#[derive(Debug)]
pub struct SimulatedStorage {
data: HashMap<String, Vec<u8>>,
bytes_written: u64,
faults: Option<FaultInjector>,
}
impl SimulatedStorage {
pub fn new() -> Self {
Self {
data: HashMap::new(),
bytes_written: 0,
faults: None,
}
}
pub fn with_faults(config: FaultConfig, seed: u64) -> Self {
Self {
data: HashMap::new(),
bytes_written: 0,
faults: Some(FaultInjector::new(config, seed)),
}
}
pub fn write(&mut self, key: &str, data: &[u8]) -> Result<(), SimStorageError> {
let new_total = self.bytes_written + data.len() as u64;
if let Some(ref fi) = self.faults
&& fi.should_crash_at(new_total)
{
return Err(SimStorageError::SimulatedCrash {
at_byte_offset: new_total,
});
}
let mut payload = data.to_vec();
if let Some(ref fi) = self.faults {
fi.try_corrupt(&mut payload);
}
self.data.insert(key.to_owned(), payload);
self.bytes_written = new_total;
Ok(())
}
pub fn read(&self, key: &str) -> Option<&[u8]> {
self.data.get(key).map(Vec::as_slice)
}
pub fn bytes_written(&self) -> u64 {
self.bytes_written
}
}
impl Default for SimulatedStorage {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn write_read_roundtrip() {
let mut s = SimulatedStorage::new();
s.write("k", b"hello").unwrap();
assert_eq!(s.read("k").unwrap(), b"hello");
}
#[test]
fn bytes_written_accumulates() {
let mut s = SimulatedStorage::new();
s.write("a", &[1, 2]).unwrap();
s.write("b", &[3]).unwrap();
assert_eq!(s.bytes_written(), 3);
}
#[test]
fn crash_at_offset_blocks_write() {
let cfg = FaultConfig::default().with_crash_at_offset(4);
let mut s = SimulatedStorage::with_faults(cfg, 0);
s.write("x", &[0; 3]).unwrap(); let err = s.write("y", &[0; 3]).unwrap_err(); assert!(matches!(err, SimStorageError::SimulatedCrash { .. }));
}
}