use crate::{Archive, Entry, Reader};
use core::cell::RefCell;
use core::convert::TryFrom;
use std::fs::File;
use std::io::{Error, Read, Seek, SeekFrom};
impl TryFrom<File> for Archive<FileReader> {
type Error = crate::Error<Error>;
fn try_from(file: File) -> Result<Self, Self::Error> {
Self::open(FileReader::from(file))
}
}
pub struct ReadSeekReader<T> {
inner: RefCell<T>,
}
impl<T> From<T> for ReadSeekReader<T> {
fn from(inner: T) -> Self {
Self::new(inner)
}
}
impl<T> ReadSeekReader<T> {
#[must_use]
pub fn new(inner: T) -> Self {
Self {
inner: RefCell::new(inner),
}
}
#[must_use]
pub fn into_inner(self) -> T {
self.inner.into_inner()
}
}
impl<T> Reader for ReadSeekReader<T>
where
T: Read + Seek,
{
type Error = Error;
fn size(&self) -> Result<u64, Self::Error> {
let mut inner = self.inner.borrow_mut();
let current = inner.stream_position()?;
let end = inner.seek(SeekFrom::End(0))?;
inner.seek(SeekFrom::Start(current))?;
Ok(end)
}
fn read_exact_at(&self, pos: u64, buf: &mut [u8]) -> Result<(), Self::Error> {
let mut inner = self.inner.borrow_mut();
inner.seek(SeekFrom::Start(pos))?;
inner.read_exact(buf)
}
}
#[cfg(unix)]
pub struct UnixFileReader {
inner: File,
}
#[cfg(unix)]
impl From<File> for UnixFileReader {
fn from(inner: File) -> Self {
Self::new(inner)
}
}
#[cfg(unix)]
impl UnixFileReader {
#[must_use]
pub fn new(inner: File) -> Self {
Self { inner }
}
#[must_use]
pub fn into_inner(self) -> File {
self.inner
}
}
#[cfg(unix)]
impl Reader for UnixFileReader {
type Error = Error;
fn size(&self) -> Result<u64, Self::Error> {
Ok(self.inner.metadata()?.len())
}
fn read_exact_at(&self, pos: u64, buf: &mut [u8]) -> Result<(), Self::Error> {
use std::os::unix::fs::FileExt;
self.inner.read_exact_at(buf, pos)
}
}
#[cfg(unix)]
pub type FileReader = UnixFileReader;
#[cfg(not(unix))]
pub type FileReader = ReadSeekReader<File>;
pub struct EntryReader<'a, R> {
archive: &'a Archive<R>,
pos: u64,
end: u64,
}
impl<'a, R: Reader> Read for EntryReader<'a, R>
where
R::Error: Into<Error>,
{
fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
if self.end <= self.pos {
return Ok(0);
};
let remaining = self.end - self.pos;
let to_read = buf.len().min(usize::try_from(remaining).unwrap());
self.archive
.reader
.read_exact_at(self.pos, &mut buf[..to_read])
.map_err(Into::into)?;
self.pos += u64::try_from(to_read).unwrap();
Ok(to_read)
}
}
impl<'a, R: Reader> Entry<'a, R> {
pub fn reader(&self) -> Result<EntryReader<'a, R>, crate::Error<R::Error>> {
let range = self.data_range()?;
Ok(EntryReader {
archive: self.archive,
pos: range.data_range.start,
end: range.data_range.end,
})
}
}