sysutils: grep for stdin

This commit is contained in:
Mark Poliakov 2025-03-01 23:52:38 +02:00
parent aefa7a93fa
commit de98ae1082

View File

@ -3,7 +3,7 @@
use std::{ use std::{
fs::File, fs::File,
io::{self, stdout, Seek, Write}, io::{self, stdout, Seek, Stdin, Write},
path::{Path, PathBuf}, path::{Path, PathBuf},
process::ExitCode, process::ExitCode,
}; };
@ -29,6 +29,22 @@ struct FileInput {
position: usize, position: usize,
} }
struct StdinInput {
stdin: Stdin,
buffer: String,
eof: bool,
}
impl StdinInput {
pub fn new() -> Self {
Self {
stdin: io::stdin(),
buffer: String::new(),
eof: false,
}
}
}
impl FileInput { impl FileInput {
pub fn open<P: AsRef<Path>>(path: P) -> io::Result<Self> { pub fn open<P: AsRef<Path>>(path: P) -> io::Result<Self> {
let mut file = File::open(path)?; let mut file = File::open(path)?;
@ -39,6 +55,22 @@ impl FileInput {
} }
} }
impl Input for StdinInput {
fn next_line(&mut self) -> io::Result<Option<&[u8]>> {
if self.eof {
return Ok(None);
}
self.buffer.clear();
if self.stdin.read_line(&mut self.buffer)? == 0 {
self.eof = true;
return Ok(None);
}
Ok(Some(self.buffer.trim_end_matches('\n').as_bytes()))
}
}
impl Input for FileInput { impl Input for FileInput {
fn next_line(&mut self) -> io::Result<Option<&[u8]>> { fn next_line(&mut self) -> io::Result<Option<&[u8]>> {
if self.position >= self.mmap.len() { if self.position >= self.mmap.len() {
@ -57,7 +89,7 @@ impl Input for FileInput {
} }
} }
fn run_on<I: Input>(input: &mut I, regex: &Regex) -> io::Result<bool> { fn run_on<I: Input>(input: &mut I, regex: &Regex) -> Result<bool, Error> {
let mut any = false; let mut any = false;
loop { loop {
let Some(line) = input.next_line()? else { let Some(line) = input.next_line()? else {
@ -76,14 +108,18 @@ fn run_on<I: Input>(input: &mut I, regex: &Regex) -> io::Result<bool> {
#[derive(Debug, Parser)] #[derive(Debug, Parser)]
struct Args { struct Args {
expression: String, expression: String,
filename: PathBuf, filename: Option<PathBuf>,
} }
fn run(args: &Args) -> Result<bool, Error> { fn run(args: &Args) -> Result<bool, Error> {
let regex = Regex::new(&args.expression)?; let regex = Regex::new(&args.expression)?;
let mut input = FileInput::open(&args.filename)?; if let Some(filename) = args.filename.as_deref() {
let any = run_on(&mut input, &regex)?; let mut input = FileInput::open(filename)?;
Ok(any) run_on(&mut input, &regex)
} else {
let mut input = StdinInput::new();
run_on(&mut input, &regex)
}
} }
fn main() -> ExitCode { fn main() -> ExitCode {