Use impl Write over a closure

This commit is contained in:
Paul Campbell 2023-03-23 08:09:58 +00:00
parent a31d0f769a
commit 20263c9474
2 changed files with 46 additions and 63 deletions

View file

@ -1,8 +1,11 @@
use clap::Parser; use clap::Parser;
use std::fmt::{Debug, Formatter}; use std::fmt::Debug;
use std::fs; use std::fs;
use std::io::Write;
use std::path::PathBuf; use std::path::PathBuf;
pub type Result<T> = std::result::Result<T, Box<dyn std::error::Error>>;
#[derive(Parser, Debug)] #[derive(Parser, Debug)]
#[command(version)] #[command(version)]
pub struct Cli { pub struct Cli {
@ -26,58 +29,44 @@ pub struct Cli {
ignore_extras: bool, ignore_extras: bool,
} }
pub struct SkipError(String); pub fn skip(cli: &Cli, writer: &mut impl Write) -> Result<()> {
impl Debug for SkipError {
fn fmt(&self, f: &mut Formatter<'_>) -> core::result::Result<(), std::fmt::Error> {
write!(f, "{}", self.0)
}
}
pub type Result<T> = core::result::Result<T, SkipError>;
pub fn skip<F>(cli: &Cli, out: F) -> Result<()>
where
F: FnMut(String) -> (),
{
match &cli.file { match &cli.file {
Some(ref file) => { Some(ref file) => {
if let Some(line) = &cli.line { if let Some(line) = &cli.line {
skip_file_lines_matching(&cli, out, file, line) skip_file_lines_matching(&cli, writer, file, line)
} else if let Some(ref token) = cli.token { } else if let Some(ref token) = cli.token {
skip_file_tokens(cli, out, file, token) skip_file_tokens(cli, writer, file, token)
} else { } else {
skip_file_lines(cli, out, file) skip_file_lines(cli, writer, file)
} }
} }
None => Err(SkipError("Not yet implemented - reading from stdin".into())), None => todo!("reading from stdin"),
} }
} }
fn skip_file_lines<F>(cli: &Cli, mut out: F, file: &PathBuf) -> Result<()> fn skip_file_lines(cli: &Cli, writer: &mut impl Write, file: &PathBuf) -> Result<()> {
where
F: FnMut(String) -> (),
{
let content = fs::read_to_string(file).expect("Could not read file"); 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 content.lines() {
if counter >= cli.lines { if counter >= cli.lines {
out(String::from(current_line)); writeln!(writer, "{}", current_line)?;
} }
counter += 1; counter += 1;
} }
Ok(()) Ok(())
} }
fn skip_file_lines_matching<F>(cli: &Cli, mut out: F, file: &PathBuf, line: &str) -> Result<()> fn skip_file_lines_matching(
where cli: &Cli,
F: FnMut(String) -> (), writer: &mut impl Write,
{ file: &PathBuf,
line: &str,
) -> Result<()> {
let content = fs::read_to_string(file).expect("Could not read file"); 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 content.lines() {
if counter >= cli.lines { if counter >= cli.lines {
out(String::from(current_line)); writeln!(writer, "{}", current_line)?;
} }
if line == current_line { if line == current_line {
counter += 1; counter += 1;
@ -86,15 +75,12 @@ where
Ok(()) Ok(())
} }
fn skip_file_tokens<F>(cli: &Cli, mut out: F, file: &PathBuf, token: &str) -> Result<()> fn skip_file_tokens(cli: &Cli, writer: &mut impl Write, file: &PathBuf, token: &str) -> Result<()> {
where
F: FnMut(String) -> (),
{
let content = fs::read_to_string(file).expect("Could not read file"); 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 content.lines() {
if counter >= cli.lines { if counter >= cli.lines {
out(String::from(current_line)); writeln!(writer, "{}", current_line)?;
} }
if current_line.contains(&token) { if current_line.contains(&token) {
if cli.ignore_extras { if cli.ignore_extras {
@ -123,13 +109,13 @@ mod tests {
token: None, token: None,
ignore_extras: false, ignore_extras: false,
}; };
let mut lines: Vec<String> = Vec::new(); let mut lines = Vec::new();
//when //when
skip(&cli, |line| lines.push(line))?; skip(&cli, &mut lines)?;
//then //then
assert_eq!(lines, vec!["line 2"]); assert_eq!(String::from_utf8(lines)?, "line 2\n");
Ok(()) Ok(())
} }
@ -143,13 +129,13 @@ mod tests {
token: None, token: None,
ignore_extras: false, ignore_extras: false,
}; };
let mut lines: Vec<String> = Vec::new(); let mut lines = Vec::new();
//when //when
skip(&cli, |line| lines.push(line))?; skip(&cli, &mut lines)?;
//then //then
assert_eq!(lines, vec!["alpha", "gamma"]); assert_eq!(String::from_utf8(lines)?, vec!["alpha", "gamma\n"].join("\n"));
Ok(()) Ok(())
} }
@ -163,13 +149,13 @@ mod tests {
token: None, token: None,
ignore_extras: false, ignore_extras: false,
}; };
let mut lines: Vec<String> = Vec::new(); let mut lines = Vec::new();
//when //when
skip(&cli, |line| lines.push(line))?; skip(&cli, &mut lines)?;
//then //then
assert_eq!(lines, vec!["gamma"]); assert_eq!(String::from_utf8(lines)?, "gamma\n");
Ok(()) Ok(())
} }
@ -183,19 +169,20 @@ mod tests {
token: Some(String::from("one")), token: Some(String::from("one")),
ignore_extras: false, ignore_extras: false,
}; };
let mut lines: Vec<String> = Vec::new(); let mut lines = Vec::new();
//when //when
skip(&cli, |line| lines.push(line))?; skip(&cli, &mut lines)?;
//then //then
assert_eq!( assert_eq!(
lines, String::from_utf8(lines)?,
vec![ vec![
"Or help one fainting robin", "Or help one fainting robin",
"Unto his nest again,", "Unto his nest again,",
"I shall not live in vain." "I shall not live in vain.\n"
] ]
.join("\n")
); );
Ok(()) Ok(())
} }
@ -210,14 +197,14 @@ mod tests {
token: Some(String::from("or")), token: Some(String::from("or")),
ignore_extras: false, ignore_extras: false,
}; };
let mut lines: Vec<String> = Vec::new(); let mut lines = Vec::new();
//when //when
skip(&cli, |line| lines.push(line))?; skip(&cli, &mut lines)?;
//then //then
assert_eq!( assert_eq!(
lines, String::from_utf8(lines)?,
vec![ vec![
//Lorem ipsum dolor sit amet, -- +2 = 2 //Lorem ipsum dolor sit amet, -- +2 = 2
//consectetur adipiscing elit, //consectetur adipiscing elit,
@ -226,8 +213,9 @@ mod tests {
"Ut enim ad minim veniam,", "Ut enim ad minim veniam,",
"quis nostrud exercitation ullamco", "quis nostrud exercitation ullamco",
"laboris nisi ut aliquip ex ea", "laboris nisi ut aliquip ex ea",
"commodo consequat." "commodo consequat.\n"
] ]
.join("\n")
); );
Ok(()) Ok(())
} }
@ -242,14 +230,14 @@ mod tests {
token: Some(String::from("or")), token: Some(String::from("or")),
ignore_extras: true, ignore_extras: true,
}; };
let mut lines: Vec<String> = Vec::new(); let mut lines = Vec::new();
//when //when
skip(&cli, |line| lines.push(line))?; skip(&cli, &mut lines)?;
//then //then
assert_eq!( assert_eq!(
lines, String::from_utf8(lines)?,
vec![ vec![
//Lorem ipsum dolor sit amet, -- 1 //Lorem ipsum dolor sit amet, -- 1
//consectetur adipiscing elit, //consectetur adipiscing elit,
@ -258,8 +246,9 @@ mod tests {
//Ut enim ad minim veniam, //Ut enim ad minim veniam,
//quis nostrud exercitation ullamco //quis nostrud exercitation ullamco
//laboris nisi ut aliquip ex ea -- 4 //laboris nisi ut aliquip ex ea -- 4
"commodo consequat." "commodo consequat.\n"
] ]
.join("\n")
); );
Ok(()) Ok(())
} }

View file

@ -1,15 +1,9 @@
use clap::Parser; use clap::Parser;
use skip::{skip, Cli, Result}; use skip::{skip, Cli, Result};
use std::io::Write;
fn main() -> Result<()> { fn main() -> Result<()> {
let cli = Cli::parse(); let cli = Cli::parse();
let mut stdout = std::io::stdout();
let stdout = std::io::stdout(); skip(&cli, &mut stdout)
let mut output = stdout.lock();
skip(&cli, |line| match writeln!(output, "{}", line) {
Err(_) => (),
Ok(_) => (),
})
} }