use crate::error::BoxedError;
use alloc::boxed::Box;
use alloc::vec::Vec;
use core::error::Error;
use core::fmt::{self, Display, Formatter};
#[cfg(feature = "std")]
use {
std::fs::File,
std::io::{Seek, SeekFrom},
};
pub trait Ext4Read {
fn read(
&mut self,
start_byte: u64,
dst: &mut [u8],
) -> Result<(), BoxedError>;
}
#[cfg(feature = "std")]
impl Ext4Read for File {
fn read(
&mut self,
start_byte: u64,
dst: &mut [u8],
) -> Result<(), BoxedError> {
use std::io::Read;
self.seek(SeekFrom::Start(start_byte)).map_err(Box::new)?;
self.read_exact(dst).map_err(Box::new)?;
Ok(())
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct MemIoError {
start: u64,
read_len: usize,
src_len: usize,
}
impl Display for MemIoError {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(
f,
"failed to read {} bytes at offset {} from a slice of length {}",
self.read_len, self.start, self.src_len
)
}
}
impl Error for MemIoError {}
impl Ext4Read for Vec<u8> {
fn read(
&mut self,
start_byte: u64,
dst: &mut [u8],
) -> Result<(), BoxedError> {
let start = usize::try_from(start_byte).map_err(|_| {
Box::new(MemIoError {
start: start_byte,
read_len: dst.len(),
src_len: self.len(),
})
})?;
let end = start + dst.len();
let src = self.get(start..end).ok_or(Box::new(MemIoError {
start: start_byte,
read_len: dst.len(),
src_len: self.len(),
}))?;
dst.copy_from_slice(src);
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_vec_read() {
let mut src = vec![1, 2, 3];
let mut dst = [0; 3];
src.read(0, &mut dst).unwrap();
assert_eq!(dst, [1, 2, 3]);
let mut dst = [0; 2];
src.read(1, &mut dst).unwrap();
assert_eq!(dst, [2, 3]);
let err = src.read(4, &mut dst).unwrap_err();
assert_eq!(
format!("{err}"),
format!(
"failed to read 2 bytes at offset 4 from a slice of length 3"
)
);
}
}