use super::data_chunk::{Chunk, ChunkSize, FileInfo};
use super::Memory;
use std::io::Seek;
use std::time::Instant;
use std::{
fs::File,
io::{self, BufReader, Read},
};
#[cfg_attr(feature = "debug", derive(Debug))]
struct FilePack<R>
where
R: Read + Seek,
{
metadata: FileInfo,
buffer: BufReader<R>,
read_complete: bool,
}
impl FilePack<File> {
fn new(buffer: BufReader<File>, start_position: usize) -> io::Result<FilePack<File>> {
Ok(FilePack {
metadata: FileInfo::new(buffer.get_ref().metadata()?.len() as f64, start_position),
buffer,
read_complete: false,
})
}
fn create_buffer(path: &str) -> io::Result<BufReader<File>> {
Ok(BufReader::new(File::open(path)?))
}
}
impl FilePack<io::Cursor<Vec<u8>>> {
fn new(
buffer: BufReader<io::Cursor<Vec<u8>>>,
start_position: usize,
) -> io::Result<FilePack<io::Cursor<Vec<u8>>>> {
Ok(FilePack {
metadata: FileInfo::new(buffer.get_ref().get_ref().len() as f64, start_position),
buffer,
read_complete: false,
})
}
pub fn create_buffer(bytes: Vec<u8>) -> io::Result<BufReader<io::Cursor<Vec<u8>>>> {
Ok(BufReader::new(io::Cursor::new(bytes)))
}
}
impl<R: Read + Seek> FilePack<R> {
fn read_chunk(&mut self) -> io::Result<Chunk> {
let mut buffer = Vec::new();
let timer = Instant::now();
self.buffer
.get_mut()
.take(self.metadata.chunk_info.prev_bytes_per_second.max(1.0) as u64)
.read_to_end(&mut buffer)?;
let timer = timer.elapsed();
if buffer.is_empty() {
self.read_complete = true;
}
Ok(Chunk {
bytes_per_second: if !timer.is_zero() {
buffer.len() as f64 / timer.as_secs_f64()
} else {
self.metadata.chunk_info.prev_bytes_per_second
},
value: buffer,
})
}
}
#[cfg_attr(feature = "debug", derive(Debug))]
pub struct FileIter<R: Seek + Read> {
memory: Memory,
file: FilePack<R>,
}
impl FileIter<File> {
pub fn new<S: Into<Box<str>>>(path: S) -> io::Result<FileIter<File>> {
Ok(FileIter {
memory: Memory::new(),
file: FilePack::<File>::new(FilePack::<File>::create_buffer(&path.into())?, 0)?,
})
}
}
impl<R: Seek + Read> FileIter<R> {
pub fn is_read_complete(&self) -> bool {
self.file.read_complete
}
pub fn get_file_size(&self) -> f64 {
self.file.metadata.size
}
pub fn set_mode(mut self, mode: ChunkSize) -> Self {
self.file.metadata.chunk_info.mode = mode;
self
}
pub fn set_start_position_bytes(mut self, position: usize) -> io::Result<Self> {
self.file.metadata.start_position = position.min(self.file.metadata.size as usize);
self.file.buffer.seek(io::SeekFrom::Start(
self.file.metadata.start_position as u64,
))?;
Ok(self)
}
pub fn set_start_position_percent(mut self, position_percent: f64) -> io::Result<Self> {
self.file.metadata.start_position =
(self.file.metadata.size * (position_percent / 100.0)).min(100.0) as usize;
self.file.buffer.seek(io::SeekFrom::Start(
self.file.metadata.start_position as u64,
))?;
Ok(self)
}
pub fn include_available_swap(mut self) -> Self {
self.memory.swap_check = true;
self
}
}
impl<R: Seek + Read> Iterator for FileIter<R> {
type Item = io::Result<Vec<u8>>;
fn next(&mut self) -> Option<Self::Item> {
self.file.metadata.chunk_info.prev_bytes_per_second = ChunkSize::calculate_chunk(
self.file.metadata.chunk_info.prev_bytes_per_second,
self.file.metadata.chunk_info.now_bytes_per_second,
self.file.metadata.size,
{
self.memory.update_ram();
self.memory.ram_available
},
self.file.metadata.chunk_info.mode,
);
match self.file.read_chunk() {
Ok(chunk) => {
self.file.metadata.chunk_info.now_bytes_per_second = chunk.bytes_per_second;
if !chunk.value.is_empty() {
Some(Ok(chunk.value))
} else {
None
}
}
Err(e) => Some(Err(e)),
}
}
}
mod impl_try_from {
use std::borrow::Cow;
use super::*;
impl TryFrom<File> for FileIter<File> {
type Error = io::Error;
fn try_from(file: File) -> Result<Self, Self::Error> {
Ok(FileIter {
memory: Memory::new(),
file: FilePack::<File>::new(BufReader::new(file), 0)?,
})
}
}
impl TryFrom<BufReader<File>> for FileIter<File> {
type Error = io::Error;
fn try_from(buffer: BufReader<File>) -> Result<Self, Self::Error> {
Ok(FileIter {
memory: Memory::new(),
file: FilePack::<File>::new(buffer, 0)?,
})
}
}
impl TryFrom<Vec<u8>> for FileIter<io::Cursor<Vec<u8>>> {
type Error = io::Error;
fn try_from(bytes: Vec<u8>) -> Result<Self, Self::Error> {
Ok(FileIter {
memory: Memory::new(),
file: FilePack::<io::Cursor<Vec<u8>>>::new(
FilePack::<io::Cursor<Vec<u8>>>::create_buffer(bytes)?,
0,
)?,
})
}
}
impl TryFrom<&Vec<u8>> for FileIter<io::Cursor<Vec<u8>>> {
type Error = io::Error;
fn try_from(bytes: &Vec<u8>) -> Result<Self, Self::Error> {
Ok(FileIter {
memory: Memory::new(),
file: FilePack::<io::Cursor<Vec<u8>>>::new(
FilePack::<io::Cursor<Vec<u8>>>::create_buffer(bytes.clone())?,
0,
)?,
})
}
}
impl TryFrom<io::Cursor<Vec<u8>>> for FileIter<io::Cursor<Vec<u8>>> {
type Error = io::Error;
fn try_from(buffer: io::Cursor<Vec<u8>>) -> Result<Self, Self::Error> {
Ok(FileIter {
memory: Memory::new(),
file: FilePack::<io::Cursor<Vec<u8>>>::new(BufReader::new(buffer), 0)?,
})
}
}
impl TryFrom<BufReader<io::Cursor<Vec<u8>>>> for FileIter<io::Cursor<Vec<u8>>> {
type Error = io::Error;
fn try_from(buffer: BufReader<io::Cursor<Vec<u8>>>) -> Result<Self, Self::Error> {
Ok(FileIter {
memory: Memory::new(),
file: FilePack::<io::Cursor<Vec<u8>>>::new(buffer, 0)?,
})
}
}
impl TryFrom<&[u8]> for FileIter<io::Cursor<Vec<u8>>> {
type Error = io::Error;
fn try_from(bytes: &[u8]) -> Result<Self, Self::Error> {
Ok(FileIter {
memory: Memory::new(),
file: FilePack::<io::Cursor<Vec<u8>>>::new(
FilePack::<io::Cursor<Vec<u8>>>::create_buffer(bytes.to_vec())?,
0,
)?,
})
}
}
#[cfg(not(tarpaulin_include))]
impl TryFrom<&str> for FileIter<File> {
type Error = io::Error;
fn try_from(path: &str) -> Result<Self, Self::Error> {
FileIter::new(path)
}
}
#[cfg(not(tarpaulin_include))]
impl TryFrom<String> for FileIter<File> {
type Error = io::Error;
fn try_from(path: String) -> Result<Self, Self::Error> {
FileIter::new(path)
}
}
#[cfg(not(tarpaulin_include))]
impl TryFrom<Cow<'_, str>> for FileIter<File> {
type Error = io::Error;
fn try_from(path: Cow<'_, str>) -> Result<Self, Self::Error> {
FileIter::new(path)
}
}
}