use std::fs::{File, Metadata};
use std::io::{Seek, BufReader, SeekFrom, Read, BufWriter, Write};
use std::collections::{VecDeque};
const BUFFER_SIZE: u64 = 4096;
pub enum ModificationType {
Added,
Removed,
NoChange,
}
#[allow(dead_code)]
pub enum Input {
File(File),
Stdin(std::io::Stdin),
}
pub struct BackwardsReader<'a> {
pieces: VecDeque<VecDeque<Vec<u8>>>,
num_of_lines: usize,
fd: &'a mut BufReader<File>,
total_newlines: usize,
first_read: bool,
last_offset: u64
}
impl<'a> BackwardsReader<'a> {
pub fn new(num_of_lines: usize, fd: &'a mut BufReader<File>) -> Self {
let last_offset = fd.seek(SeekFrom::End(0))
.unwrap_or_else(|_| { panic!("Failed to seek to end of file") });
BackwardsReader {
pieces: VecDeque::with_capacity(num_of_lines),
num_of_lines: num_of_lines,
fd: fd,
total_newlines: 0,
first_read: true,
last_offset: last_offset
}
}
fn handle_partial_read(&mut self) {
if self.last_offset > 0 {
self.fd.seek(SeekFrom::Start(0)).unwrap();
let mut buff = vec![0; (self.last_offset) as usize];
self.fd.read_exact(buff.as_mut_slice())
.unwrap_or_else(|_| { panic!("Incorrectly handled unexpected EOF. Probably an off by one error") });
if self.first_read && buff[buff.len() - 1] != b'\n' {
self.total_newlines += 1;
self.first_read = false;
buff.push(b'\n');
}
let buff: VecDeque<Vec<u8>> = buff.split(|elm: &u8| {*elm == b'\n'}).map(|elm: &[u8]| elm.to_vec()).collect();
self.total_newlines += buff.len() - 1;
self.pieces.push_front(buff);
}
}
fn read(&mut self) -> bool {
let seek_offset = if (self.last_offset as i64) - (BUFFER_SIZE as i64) >= 0 {
self.last_offset - BUFFER_SIZE
} else {
self.handle_partial_read();
return false;
};
match self.fd.seek(SeekFrom::Start(seek_offset)) {
Ok(new_offset) => {
self.last_offset = new_offset;
},
Err(_) => {
self.handle_partial_read();
return false;
}
}
let mut buff = vec![0; BUFFER_SIZE as usize];
self.fd.read_exact(buff.as_mut_slice())
.unwrap_or_else(|_| { panic!("Failed to read from end of file in BackwardsReader") });
if self.first_read && buff[buff.len() - 1] != b'\n' {
self.total_newlines += 1;
self.first_read = false;
buff.push(b'\n');
}
let buff: VecDeque<Vec<u8>> = buff.split(|elm: &u8| {*elm == b'\n'}).map(|elm: &[u8]| elm.to_vec()).collect();
self.total_newlines += buff.len() - 1;
self.pieces.push_front(buff);
if self.first_read {
self.first_read = false;
}
self.total_newlines < self.num_of_lines
}
pub fn read_all<T: Write>(&mut self, writer: &mut BufWriter<T>) {
while self.read() {}
if self.total_newlines > self.num_of_lines {
let mut first_chunk = self.pieces.pop_front().unwrap();
let pieces_to_discard = self.total_newlines - self.num_of_lines as usize;
if pieces_to_discard > 0 {
for _ in 0..pieces_to_discard {
first_chunk.pop_front().unwrap();
}
self.total_newlines -= pieces_to_discard;
}
self.pieces.push_front(first_chunk);
}
if self.pieces.is_empty() { return; }
let mut line: Vec<u8> = Vec::new();
while let Some(mut piece) = self.pieces.pop_front() {
if piece.len() == 1 {
line.append(piece.pop_front().unwrap().as_mut());
} else if piece.len() > 1 {
let mut last_chunk = piece.pop_back().unwrap();
for mut chunk in piece {
line.append(&mut chunk);
line.push(b'\n');
writer.write(&line).unwrap();
line.clear();
}
line.append(&mut last_chunk);
}
}
if !line.is_empty() {
writer.write(&line).unwrap();
}
}
}
#[derive(Debug)]
pub struct StatefulFile {
pub fd: BufReader<File>,
pub old_metadata: Metadata,
file_name: String,
cursor: SeekFrom,
}
impl StatefulFile {
pub fn new(fd: File, file_name: String) -> Self {
StatefulFile {
old_metadata: fd.metadata()
.unwrap_or_else(|_| { panic!("Could not retrieve metadata for file: {}", &file_name) }),
fd: BufReader::new(fd),
file_name: file_name,
cursor: SeekFrom::Start(0),
}
}
pub fn update_metadata(&mut self) {
self.old_metadata = self.fd.get_ref().metadata()
.unwrap_or_else(|_| { panic!("Could not retrieve metadata for file: {}", self.file_name) });
}
pub fn modification_type(&self) -> ModificationType {
let new_metadata = self.fd.get_ref().metadata()
.unwrap_or_else(|_| { panic!("Could not retrieve metadata for file: {}", self.file_name) });
if new_metadata.len() > self.old_metadata.len() {
ModificationType::Added
} else if new_metadata.len() < self.old_metadata.len() {
ModificationType::Removed
} else {
ModificationType::NoChange
}
}
pub fn seek_to_cursor(&mut self) {
self.fd.seek(self.cursor).unwrap();
}
pub fn update_cursor(&mut self) {
self.cursor = SeekFrom::Start(self.fd.seek(SeekFrom::Current(0)).unwrap());
}
pub fn reset_cursor(&mut self) {
self.cursor = SeekFrom::Start(0);
}
}