use crate::Arguments;
use num_cpus;
use std::cmp::max;
use std::fs;
use std::io::{BufRead, BufReader, Write};
use std::sync::mpsc;
use std::thread;
use std::{fs::File, path::PathBuf};
use tempfile::NamedTempFile;
use tivilsta::Ruler;
use crate::utils;
#[derive(Debug)]
struct CLIHandlerSettings {
output_given: bool,
}
#[derive(Debug)]
struct CLIHandlerTmp {
output: NamedTempFile,
}
#[derive(Debug)]
struct CLIHandlerPaths {
source: PathBuf,
output: PathBuf,
whitelist: Vec<String>,
all_prefixed: Vec<String>,
reg_prefixed: Vec<String>,
rzd_prefixed: Vec<String>,
tmps: Vec<String>,
}
#[derive(Debug)]
pub struct CLIHandler {
source: File,
whitelist: Vec<File>,
all_prefixed: Vec<File>,
reg_prefixed: Vec<File>,
rzd_prefixed: Vec<File>,
ruler: Ruler,
settings: CLIHandlerSettings,
tmp: CLIHandlerTmp,
paths: CLIHandlerPaths,
pub multithread: bool,
max_threads: usize,
}
impl CLIHandler {
pub fn new(args: Arguments) -> CLIHandler {
let mut paths = CLIHandlerPaths {
source: PathBuf::new(),
output: PathBuf::new(),
whitelist: vec![],
all_prefixed: vec![],
reg_prefixed: vec![],
rzd_prefixed: vec![],
tmps: vec![],
};
let tmp = CLIHandlerTmp {
output: NamedTempFile::new().unwrap(),
};
let mut settings = CLIHandlerSettings {
output_given: false,
};
settings.output_given = args.output.is_some();
paths.source = args.source;
paths.output = args.output.unwrap_or_default();
let mut whitelist: Vec<File> = vec![];
let mut all_prefixed: Vec<File> = vec![];
let mut reg_prefixed: Vec<File> = vec![];
let mut rzd_prefixed: Vec<File> = vec![];
if !args.whitelist.is_empty() {
for file in args.whitelist {
let (path, downloaded) = utils::download_file(&file);
if downloaded {
paths.tmps.push(path.clone())
}
whitelist.push(File::open(&path).unwrap());
paths.whitelist.push(path.clone());
}
}
if !args.all.is_empty() {
for file in args.all {
let (path, downloaded) = utils::download_file(&file);
if downloaded {
paths.tmps.push(path.clone())
}
all_prefixed.push(File::open(&path).unwrap());
paths.all_prefixed.push(path.clone())
}
}
if !args.reg.is_empty() {
for file in args.reg {
let (path, downloaded) = utils::download_file(&file);
if downloaded {
paths.tmps.push(path.clone())
}
reg_prefixed.push(File::open(&path).unwrap());
paths.reg_prefixed.push(path.clone())
}
}
if !args.rzd.is_empty() {
for file in args.rzd {
let (path, downloaded) = utils::download_file(&file);
if downloaded {
paths.tmps.push(path.clone())
}
rzd_prefixed.push(File::open(&path).unwrap());
paths.rzd_prefixed.push(path.clone())
}
}
let mut result = CLIHandler {
source: File::open(&paths.source).unwrap(),
whitelist,
all_prefixed,
reg_prefixed,
rzd_prefixed,
ruler: Ruler::new(args.allow_complements),
settings,
tmp,
paths,
multithread: args.multithread,
max_threads: args.max_threads.unwrap_or(max(1, num_cpus::get() - 2)),
};
result.load_all();
result
}
fn load_whitelist(&mut self) -> bool {
for file in &self.whitelist {
let whitelist_file = BufReader::new(file);
for line in whitelist_file.lines() {
self.ruler.parse(&line.unwrap())
}
}
for file in &self.all_prefixed {
let whitelist_file = BufReader::new(file);
for line in whitelist_file.lines() {
self.ruler.parse(&format!("ALL {}", &line.unwrap()))
}
}
for file in &self.reg_prefixed {
let whitelist_file = BufReader::new(file);
for line in whitelist_file.lines() {
self.ruler.parse(&format!("REG {}", &line.unwrap()))
}
}
for file in &self.rzd_prefixed {
let whitelist_file = BufReader::new(file);
for line in whitelist_file.lines() {
self.ruler.parse(&format!("RZD {}", &line.unwrap()))
}
}
true
}
pub fn load_all(&mut self) -> bool {
self.load_whitelist()
}
fn write_final_destination(&self) {
if self.settings.output_given {
let _ = fs::copy(self.tmp.output.path(), &self.paths.output).unwrap();
}
}
pub fn cleanup(&mut self) -> bool {
let source = self.source.try_clone().unwrap();
let src = BufReader::new(source);
for line in src.lines() {
let line = self.ruler.idnaze_line(&line.unwrap());
if self.ruler.is_whitelisted(&line) {
continue;
}
let _ = self
.tmp
.output
.write((line.to_string() + "\n").as_bytes())
.unwrap();
if !self.settings.output_given {
println!("{}", &line)
}
}
self.write_final_destination();
true
}
pub fn multithreaded_cleanup(&mut self) -> bool {
let source = self.source.try_clone().unwrap();
let src = BufReader::new(source);
let (line_sender, line_receiver) = mpsc::channel();
let (output_sender, output_receiver) = mpsc::channel();
let line_receiver = std::sync::Arc::new(std::sync::Mutex::new(line_receiver));
let mut worker_handles = vec![];
for _ in 0..max(1, self.max_threads.saturating_sub(2)) {
let output_sender = output_sender.clone();
let line_receiver = std::sync::Arc::clone(&line_receiver);
let mut ruler = self.ruler.clone();
let handle = thread::spawn(move || {
while let Ok(line) = line_receiver.lock().unwrap().recv() {
let processed_line = ruler.idnaze_line(&line);
if !ruler.is_whitelisted(&processed_line) {
output_sender.send(processed_line).unwrap();
}
}
});
worker_handles.push(handle);
}
let sender_handle = thread::spawn(move || {
for line in src.lines() {
let line = line.unwrap();
line_sender.send(line).unwrap();
}
drop(line_sender); });
drop(output_sender);
let output_path = self.tmp.output.path().to_path_buf();
let stdout_output = !self.settings.output_given;
let output_handle = thread::spawn(move || {
let mut output_file = File::create(output_path).unwrap();
for line in output_receiver {
let _ = output_file
.write((line.to_string() + "\n").as_bytes())
.unwrap();
if stdout_output {
println!("{}", &line)
}
}
});
sender_handle.join().unwrap();
for handle in worker_handles {
handle.join().unwrap();
}
output_handle.join().unwrap();
self.write_final_destination();
true
}
}
impl Drop for CLIHandler {
fn drop(&mut self) {
for file in &self.paths.tmps {
let _ = fs::remove_file(file);
}
}
}