1
Fork 0
mirror of https://github.com/RGBCube/uutils-coreutils synced 2025-07-28 11:37:44 +00:00

wc: add lines() method for iterating over lines

Add the `WordCountable::lines()` method that returns an iterator over
lines of a file-like object. This mirrors the
`std::io::BufRead::lines()` method, with some minor differences due to
the particular use case of `wc`.

This commit also creates a new module, `countable.rs`, to contain the
`WordCountable` trait and the new `Lines` struct returned by `lines()`.
This commit is contained in:
Jeffrey Finkelstein 2021-05-02 16:29:34 -04:00
parent 0dfb3e36ab
commit 0a3e2216d7
2 changed files with 81 additions and 46 deletions

View file

@ -0,0 +1,72 @@
//! Traits and implementations for iterating over lines in a file-like object.
//!
//! This module provides a [`WordCountable`] trait and implementations
//! for some common file-like objects. Use the [`WordCountable::lines`]
//! method to get an iterator over lines of a file-like object.
use std::fs::File;
use std::io::{self, BufRead, BufReader, Read, StdinLock};
#[cfg(unix)]
use std::os::unix::io::AsRawFd;
#[cfg(unix)]
pub trait WordCountable: AsRawFd + Read {
type Buffered: BufRead;
fn lines(self) -> Lines<Self::Buffered>;
}
#[cfg(not(unix))]
pub trait WordCountable: Read {
type Buffered: BufRead;
fn lines(self) -> Lines<Self::Buffered>;
}
impl WordCountable for StdinLock<'_> {
type Buffered = Self;
fn lines(self) -> Lines<Self::Buffered>
where
Self: Sized,
{
Lines { buf: self }
}
}
impl WordCountable for File {
type Buffered = BufReader<Self>;
fn lines(self) -> Lines<Self::Buffered>
where
Self: Sized,
{
Lines {
buf: BufReader::new(self),
}
}
}
/// An iterator over the lines of an instance of `BufRead`.
///
/// Similar to [`io::Lines`] but yields each line as a `Vec<u8>` and
/// includes the newline character (`\n`, the `0xA` byte) that
/// terminates the line.
///
/// [`io::Lines`]:: io::Lines
pub struct Lines<B> {
buf: B,
}
impl<B: BufRead> Iterator for Lines<B> {
type Item = io::Result<Vec<u8>>;
fn next(&mut self) -> Option<Self::Item> {
let mut line = Vec::new();
// reading from a TTY seems to raise a condition on, rather than return Some(0) like a file.
// hence the option wrapped in a result here
match self.buf.read_until(b'\n', &mut line) {
Ok(0) => None,
Ok(_n) => Some(Ok(line)),
Err(e) => Some(Err(e)),
}
}
}

View file

@ -11,17 +11,17 @@
extern crate uucore;
mod count_bytes;
mod countable;
use count_bytes::count_bytes_fast;
use countable::WordCountable;
use clap::{App, Arg, ArgMatches};
use thiserror::Error;
use std::cmp::max;
use std::fs::File;
use std::io::{self, BufRead, BufReader, Read, StdinLock, Write};
use std::io::{self, Write};
use std::ops::{Add, AddAssign};
#[cfg(unix)]
use std::os::unix::io::AsRawFd;
use std::path::Path;
use std::str::from_utf8;
@ -82,32 +82,6 @@ impl Settings {
}
}
#[cfg(unix)]
trait WordCountable: AsRawFd + Read {
type Buffered: BufRead;
fn get_buffered(self) -> Self::Buffered;
}
#[cfg(not(unix))]
trait WordCountable: Read {
type Buffered: BufRead;
fn get_buffered(self) -> Self::Buffered;
}
impl WordCountable for StdinLock<'_> {
type Buffered = Self;
fn get_buffered(self) -> Self::Buffered {
self
}
}
impl WordCountable for File {
type Buffered = BufReader<Self>;
fn get_buffered(self) -> Self::Buffered {
BufReader::new(self)
}
}
#[derive(Debug, Default, Copy, Clone)]
struct WordCount {
bytes: usize,
@ -270,25 +244,16 @@ fn word_count_from_reader<T: WordCountable>(
let mut byte_count: usize = 0;
let mut char_count: usize = 0;
let mut longest_line_length: usize = 0;
let mut raw_line = Vec::new();
let mut ends_lf: bool;
// reading from a TTY seems to raise a condition on, rather than return Some(0) like a file.
// hence the option wrapped in a result here
let mut buffered_reader = reader.get_buffered();
loop {
match buffered_reader.read_until(LF, &mut raw_line) {
Ok(n) => {
if n == 0 {
break;
}
}
Err(ref e) => {
if !raw_line.is_empty() {
for line_result in reader.lines() {
let raw_line = match line_result {
Ok(l) => l,
Err(e) => {
show_warning!("Error while reading {}: {}", path, e);
} else {
break;
}
continue;
}
};
@ -317,8 +282,6 @@ fn word_count_from_reader<T: WordCountable>(
longest_line_length = current_char_count - (ends_lf as usize);
}
}
raw_line.truncate(0);
}
Ok(WordCount {