User BufRead to avoid loading entire file into memory and also support stdin
This commit is contained in:
parent
20263c9474
commit
85896bdb86
2 changed files with 66 additions and 53 deletions
54
src/lib.rs
54
src/lib.rs
|
@ -1,7 +1,7 @@
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
use std::fs;
|
use std::fs::File;
|
||||||
use std::io::Write;
|
use std::io::{BufRead, BufReader, Write};
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
pub type Result<T> = std::result::Result<T, Box<dyn std::error::Error>>;
|
pub type Result<T> = std::result::Result<T, Box<dyn std::error::Error>>;
|
||||||
|
@ -12,7 +12,7 @@ pub struct Cli {
|
||||||
/// The number of lines to skip
|
/// The number of lines to skip
|
||||||
lines: usize,
|
lines: usize,
|
||||||
/// The file to read, or stdin if not given
|
/// The file to read, or stdin if not given
|
||||||
file: Option<PathBuf>,
|
pub file: Option<PathBuf>,
|
||||||
/// Skip until N lines matching this
|
/// Skip until N lines matching this
|
||||||
#[arg(short, long, conflicts_with = "token")]
|
#[arg(short, long, conflicts_with = "token")]
|
||||||
line: Option<String>,
|
line: Option<String>,
|
||||||
|
@ -30,41 +30,44 @@ pub struct Cli {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn skip(cli: &Cli, writer: &mut impl Write) -> Result<()> {
|
pub fn skip(cli: &Cli, writer: &mut impl Write) -> Result<()> {
|
||||||
match &cli.file {
|
let reader: Box<dyn BufRead> = match &cli.file {
|
||||||
Some(ref file) => {
|
Some(ref file) => {
|
||||||
|
let file = File::open(file)?;
|
||||||
|
Box::new(BufReader::new(file))
|
||||||
|
}
|
||||||
|
None => Box::new(BufReader::new(std::io::stdin())),
|
||||||
|
};
|
||||||
if let Some(line) = &cli.line {
|
if let Some(line) = &cli.line {
|
||||||
skip_file_lines_matching(&cli, writer, file, line)
|
skip_file_lines_matching(&cli, reader, writer, line)
|
||||||
} else if let Some(ref token) = cli.token {
|
} else if let Some(ref token) = cli.token {
|
||||||
skip_file_tokens(cli, writer, file, token)
|
skip_file_tokens(cli, reader, writer, token)
|
||||||
} else {
|
} else {
|
||||||
skip_file_lines(cli, writer, file)
|
skip_file_lines(cli, reader, writer)
|
||||||
}
|
|
||||||
}
|
|
||||||
None => todo!("reading from stdin"),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn skip_file_lines(cli: &Cli, writer: &mut impl Write, file: &PathBuf) -> Result<()> {
|
fn skip_file_lines(cli: &Cli, reader: Box<dyn BufRead>, writer: &mut impl Write) -> Result<()> {
|
||||||
let content = fs::read_to_string(file).expect("Could not read file");
|
|
||||||
let mut counter = 0usize;
|
let mut counter = 0usize;
|
||||||
for current_line in content.lines() {
|
for current_line in reader.lines() {
|
||||||
|
if let Ok(current_line) = current_line {
|
||||||
if counter >= cli.lines {
|
if counter >= cli.lines {
|
||||||
writeln!(writer, "{}", current_line)?;
|
writeln!(writer, "{}", current_line)?;
|
||||||
}
|
}
|
||||||
counter += 1;
|
counter += 1;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn skip_file_lines_matching(
|
fn skip_file_lines_matching(
|
||||||
cli: &Cli,
|
cli: &Cli,
|
||||||
|
reader: Box<dyn BufRead>,
|
||||||
writer: &mut impl Write,
|
writer: &mut impl Write,
|
||||||
file: &PathBuf,
|
|
||||||
line: &str,
|
line: &str,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let content = fs::read_to_string(file).expect("Could not read file");
|
|
||||||
let mut counter = 0usize;
|
let mut counter = 0usize;
|
||||||
for current_line in content.lines() {
|
for current_line in reader.lines() {
|
||||||
|
if let Ok(current_line) = current_line {
|
||||||
if counter >= cli.lines {
|
if counter >= cli.lines {
|
||||||
writeln!(writer, "{}", current_line)?;
|
writeln!(writer, "{}", current_line)?;
|
||||||
}
|
}
|
||||||
|
@ -72,13 +75,20 @@ fn skip_file_lines_matching(
|
||||||
counter += 1;
|
counter += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn skip_file_tokens(cli: &Cli, writer: &mut impl Write, file: &PathBuf, token: &str) -> Result<()> {
|
fn skip_file_tokens(
|
||||||
let content = fs::read_to_string(file).expect("Could not read file");
|
cli: &Cli,
|
||||||
|
reader: Box<dyn BufRead>,
|
||||||
|
writer: &mut impl Write,
|
||||||
|
token: &str,
|
||||||
|
) -> Result<()> {
|
||||||
let mut counter = 0usize;
|
let mut counter = 0usize;
|
||||||
for current_line in content.lines() {
|
|
||||||
|
for current_line in reader.lines() {
|
||||||
|
if let Ok(current_line) = current_line {
|
||||||
if counter >= cli.lines {
|
if counter >= cli.lines {
|
||||||
writeln!(writer, "{}", current_line)?;
|
writeln!(writer, "{}", current_line)?;
|
||||||
}
|
}
|
||||||
|
@ -91,6 +101,7 @@ fn skip_file_tokens(cli: &Cli, writer: &mut impl Write, file: &PathBuf, token: &
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -135,7 +146,10 @@ mod tests {
|
||||||
skip(&cli, &mut lines)?;
|
skip(&cli, &mut lines)?;
|
||||||
|
|
||||||
//then
|
//then
|
||||||
assert_eq!(String::from_utf8(lines)?, vec!["alpha", "gamma\n"].join("\n"));
|
assert_eq!(
|
||||||
|
String::from_utf8(lines)?,
|
||||||
|
vec!["alpha", "gamma\n"].join("\n")
|
||||||
|
);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,5 @@ use skip::{skip, Cli, Result};
|
||||||
fn main() -> Result<()> {
|
fn main() -> Result<()> {
|
||||||
let cli = Cli::parse();
|
let cli = Cli::parse();
|
||||||
let mut stdout = std::io::stdout();
|
let mut stdout = std::io::stdout();
|
||||||
|
|
||||||
skip(&cli, &mut stdout)
|
skip(&cli, &mut stdout)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue