use std::fs::{File};
use std::io::{Read, Seek, SeekFrom};
use std::io::{BufRead, BufReader};
pub const BLOCK_SIZE: u64 = 1 << 16;
pub struct ReverseChunks<'a> {
file: &'a File,
size: u64,
max_blocks_to_read: usize,
block_idx: usize,
}
impl<'a> ReverseChunks<'a> {
pub fn new(file: &'a mut File) -> ReverseChunks<'a> {
let size = file.seek(SeekFrom::End(0)).unwrap();
let max_blocks_to_read = (size as f64 / BLOCK_SIZE as f64).ceil() as usize;
let block_idx = 0;
ReverseChunks {
file,
size,
max_blocks_to_read,
block_idx,
}
}
}
impl<'a> Iterator for ReverseChunks<'a> {
type Item = Vec<u8>;
fn next(&mut self) -> Option<Self::Item> {
if self.block_idx >= self.max_blocks_to_read {
return None;
}
let block_size = if self.block_idx == self.max_blocks_to_read - 1 {
self.size % BLOCK_SIZE
} else {
BLOCK_SIZE
};
let mut buf = vec![0; BLOCK_SIZE as usize];
let pos = self
.file
.seek(SeekFrom::Current(-(block_size as i64)))
.unwrap();
self.file
.read_exact(&mut buf[0..(block_size as usize)])
.unwrap();
let pos2 = self
.file
.seek(SeekFrom::Current(-(block_size as i64)))
.unwrap();
assert_eq!(pos, pos2);
self.block_idx += 1;
Some(buf[0..(block_size as usize)].to_vec())
}
}
pub fn backward(file: &mut File, num_delimiters: u64, delimiter: u8) {
let mut counter = 0;
for (block_idx, slice) in ReverseChunks::new(file).enumerate() {
let mut iter = slice.iter().enumerate().rev();
if block_idx == 0 {
if let Some(c) = slice.last() {
if *c == delimiter {
iter.next();
}
}
}
for (i, ch) in iter {
if *ch == delimiter {
counter += 1;
if counter >= num_delimiters {
file.seek(SeekFrom::Current((i + 1) as i64)).unwrap();
return;
}
}
}
}
}
pub fn extract_from_line(line: &str, begin_delimiter: &str, end_delimiter: &str) -> String {
let mut value = line;
if begin_delimiter.is_empty() == true && end_delimiter.is_empty() == true {
value = line;
}
if begin_delimiter.is_empty() == false && end_delimiter.is_empty() == false {
value = line
.split(begin_delimiter)
.nth(1)
.unwrap()
.split(end_delimiter)
.next()
.unwrap();
}
return value.to_string();
}
pub fn parse_line(line: &str, begin_delimiter: &str, end_delimiter: &str)-> String {
let r = extract_from_line(line, begin_delimiter, end_delimiter);
return r;
}
pub fn count_lines(path: &str) -> u64 {
let file = BufReader::new(File::open(path).expect("Unable to open file"));
let mut cnt = 0;
for _ in file.lines() {
cnt = cnt + 1;
}
return cnt;
}
pub fn match_lines(matchers: &[&str], line: &str) -> bool {
for m in matchers {
if line.contains(m){
return true;
}
}
return false;
}
pub fn extract_lines(lines: String, begin_delimiter: &str, end_delimiter: &str, search: &str, ignore: &str) -> Vec<String> {
let mut parsed_lines = Vec::new();
let search_lines: Vec<&str> = search
.split(",")
.map(|s| s.trim())
.filter(|s| !s.is_empty())
.collect::<Vec<_>>();
let ignore_lines: Vec<&str> = ignore
.split(",")
.map(|s| s.trim())
.filter(|s| !s.is_empty())
.collect::<Vec<_>>();
for line in lines.split("\n") {
if ignore != "" {
if match_lines(&ignore_lines, line) {
continue;
}
}
if match_lines(&search_lines, line) {
let parsed_line = extract_from_line(line, begin_delimiter, end_delimiter);
if !parsed_line.is_empty() {
let result_line;
let trim_line = parsed_line.trim();
if trim_line.len() < parsed_line.len() {
result_line = trim_line.to_string();
}
else{
result_line = parsed_line;
}
parsed_lines.push(result_line);
}
}
}
return parsed_lines;
}
pub fn fsearch(filename: &str, search: &str, ignore: &str, ignore_mode: bool, number_of_lines: u64) -> Vec<String> {
let mut n = number_of_lines;
if n == 0 {
n = count_lines(filename);
}
let r;
let mut contents = File::open(&filename)
.expect("Something went wrong reading the file");
if ignore_mode {
r = tail_parse(&mut contents, n, "", "", search, ignore);
}
else{
r = tail_parse(&mut contents, n, "", "", search, "");
}
return r;
}
pub fn fsearch_last_line(filename: &str, search: &str, ignore: &str, ignore_mode: bool) -> Vec<String> {
let mut counter = 0u64;
let mut search_found;
loop {
counter += 1;
if ignore_mode {
search_found = fsearch(filename, search, ignore, true, counter);
}
else{
search_found = fsearch(filename, search, "", false, counter);
}
if !search_found.is_empty() {
break;
}
}
return search_found;
}
pub fn search_last_line(filename: &str, search: &str) -> Vec<String> {
let r = fsearch_last_line(filename, search, "", false);
return r;
}
pub fn isearch_last_line(filename: &str, search: &str, ignore: &str) -> Vec<String> {
let r = fsearch_last_line(filename, search, ignore, true);
return r;
}
pub fn search_lines(filename: &str, search: &str, number_of_lines: u64) -> Vec<String> {
let r = fsearch(filename, search, "", false, number_of_lines);
return r;
}
pub fn search_all(filename: &str, search: &str) -> Vec<String> {
let r = fsearch(filename, search, "", false, 0);
return r;
}
pub fn isearch_all(filename: &str, search: &str, ignore: &str) -> Vec<String> {
let ignore_mode = true;
let r = fsearch(filename, search, ignore, ignore_mode, 0);
return r;
}
pub fn isearch_lines(filename: &str, search: &str, ignore: &str, number_of_lines: u64) -> Vec<String> {
let ignore_mode = true;
let r = fsearch(filename, search, ignore, ignore_mode, number_of_lines);
return r;
}
pub fn tail_parse(file: &mut File, lines_number: u64, begin_delimiter: &str, end_delimiter: &str, search: &str, ignore: &str) -> Vec<String> {
let delimiter = b'\n';
backward(file, lines_number, delimiter);
let mut lines = String::new();
file.read_to_string(&mut lines).expect("The string cannot be read");
let vec = extract_lines(lines, begin_delimiter, end_delimiter, search, ignore);
return vec;
}
pub fn lines(filename: &str, number_of_lines: u64) -> Vec<String> {
let mut n = number_of_lines;
if n == 0 {
n = count_lines(filename);
}
let mut contents = File::open(&filename)
.expect("Something went wrong reading the file");
let r = tail_parse(&mut contents, n, "", "", "", "");
return r;
}