use crate::block::{BlockDevice, BlockRead};
use crate::error::Result;
pub struct ReadOnlyDevice<T> {
inner: T,
}
impl<T> ReadOnlyDevice<T> {
pub fn new(inner: T) -> Self {
Self { inner }
}
pub fn inner(&self) -> &T {
&self.inner
}
pub fn into_inner(self) -> T {
self.inner
}
}
impl<T: BlockRead> BlockRead for ReadOnlyDevice<T> {
fn read_at(&self, offset: u64, buf: &mut [u8]) -> Result<()> {
self.inner.read_at(offset, buf)
}
fn size_bytes(&self) -> u64 {
self.inner.size_bytes()
}
}
impl<T: BlockRead> BlockDevice for ReadOnlyDevice<T> {}
#[cfg(test)]
mod tests {
use super::*;
use crate::error::Error;
use std::sync::Mutex;
struct WritableBytes(Mutex<Vec<u8>>);
impl BlockRead for WritableBytes {
fn read_at(&self, offset: u64, buf: &mut [u8]) -> Result<()> {
let b = self.0.lock().unwrap();
let s = offset as usize;
buf.copy_from_slice(&b[s..s + buf.len()]);
Ok(())
}
fn size_bytes(&self) -> u64 {
self.0.lock().unwrap().len() as u64
}
}
impl BlockDevice for WritableBytes {
fn write_at(&self, offset: u64, buf: &[u8]) -> Result<()> {
let mut b = self.0.lock().unwrap();
let s = offset as usize;
b[s..s + buf.len()].copy_from_slice(buf);
Ok(())
}
fn is_writable(&self) -> bool {
true
}
}
#[test]
fn read_through_works() {
let mut v = vec![0u8; 16];
v[4..8].copy_from_slice(&[0xAA, 0xBB, 0xCC, 0xDD]);
let wrapped = ReadOnlyDevice::new(WritableBytes(Mutex::new(v)));
let mut buf = [0u8; 4];
wrapped.read_at(4, &mut buf).unwrap();
assert_eq!(buf, [0xAA, 0xBB, 0xCC, 0xDD]);
}
#[test]
fn writes_rejected_even_though_inner_is_writable() {
let wrapped = ReadOnlyDevice::new(WritableBytes(Mutex::new(vec![0u8; 16])));
assert!(!BlockDevice::is_writable(&wrapped));
match BlockDevice::write_at(&wrapped, 0, &[0xFFu8; 4]) {
Err(Error::ReadOnly) => {}
other => panic!("expected ReadOnly, got {other:?}"),
}
}
#[test]
fn into_inner_returns_unchanged() {
let inner = WritableBytes(Mutex::new(vec![0u8; 8]));
let wrapped = ReadOnlyDevice::new(inner);
let back = wrapped.into_inner();
assert!(BlockDevice::is_writable(&back));
}
}