use bytelines::ByteLinesReader;
mod options;
mod statistics;
use crate::options::Options;
use crate::statistics::Stats;
use runiq::Filter;
use std::env;
use std::fs::File;
use std::io::{self, BufReader, Read, Write};
const EOL: &[u8; 1] = &[b'\n'];
fn main() -> io::Result<()> {
let result = run();
if let Err(ref err) = result {
if err.kind() != io::ErrorKind::BrokenPipe {
return result;
}
}
Ok(())
}
fn run() -> io::Result<()> {
let options = Options::from(env::args_os());
let stdin = io::stdin();
let stdout = io::stdout();
let readers: Vec<Box<dyn Read>> = options
.inputs
.iter()
.map(|input| -> Box<dyn Read> {
match input.as_ref() {
"-" => Box::new(stdin.lock()),
any => Box::new(File::open(any).unwrap()),
}
})
.collect();
let mut filter: Box<dyn Filter> = options.filter.into();
let mut statistics = Stats::new();
let mut stdout = stdout.lock();
for reader in readers {
let mut lines = BufReader::new(reader).byte_lines();
while let Some(input) = lines.next().transpose()? {
if options.statistics {
statistics.add_size(input.len() + 1)
}
if filter.detect(input) {
if options.statistics {
statistics.add_unique();
} else if !options.inverted {
stdout.write_all(input)?;
stdout.write_all(EOL)?;
}
} else {
if options.statistics {
statistics.add_duplicate();
} else if options.inverted {
stdout.write_all(input)?;
stdout.write_all(EOL)?;
}
}
}
}
if options.statistics {
statistics.print();
}
stdout.flush()?;
Ok(())
}