1
Fork 0
mirror of https://github.com/RGBCube/uutils-coreutils synced 2025-07-29 03:57:44 +00:00

uucore: move lines.rs to be a uucore feature

Refactor the `lines.rs` module to be a feature in `uucore`. It was
common to both `head` and `tail`.
This commit is contained in:
Jeffrey Finkelstein 2022-01-29 22:24:27 -05:00
parent cba0696b90
commit b9c2066ee9
9 changed files with 22 additions and 167 deletions

View file

@ -17,7 +17,7 @@ path = "src/head.rs"
[dependencies]
clap = { version = "3.0", features = ["wrap_help", "cargo"] }
memchr = "2"
uucore = { version=">=0.0.11", package="uucore", path="../../uucore", features=["ringbuffer"] }
uucore = { version=">=0.0.11", package="uucore", path="../../uucore", features=["ringbuffer", "lines"] }
[[bin]]
name = "head"

View file

@ -11,6 +11,7 @@ use std::ffi::OsString;
use std::io::{self, BufWriter, ErrorKind, Read, Seek, SeekFrom, Write};
use uucore::display::Quotable;
use uucore::error::{FromIo, UError, UResult, USimpleError};
use uucore::lines::lines;
use uucore::show;
const BUF_SIZE: usize = 65536;
@ -35,11 +36,8 @@ mod options {
pub const ZERO_NAME: &str = "ZERO";
pub const FILES_NAME: &str = "FILE";
}
mod lines;
mod parse;
mod take;
use lines::lines;
use lines::zlines;
use take::take_all_but;
use take::take_lines;
@ -282,12 +280,14 @@ fn read_but_last_n_lines(
if zero {
let stdout = std::io::stdout();
let mut stdout = stdout.lock();
for bytes in take_all_but(zlines(input), n) {
for bytes in take_all_but(lines(input, b'\0'), n) {
stdout.write_all(&bytes?)?;
}
} else {
for line in take_all_but(lines(input), n) {
print!("{}", line?);
let stdout = std::io::stdout();
let mut stdout = stdout.lock();
for bytes in take_all_but(lines(input, b'\n'), n) {
stdout.write_all(&bytes?)?;
}
}
Ok(())

View file

@ -1,151 +0,0 @@
// * This file is part of the uutils coreutils package.
// *
// * For the full copyright and license information, please view the LICENSE
// * file that was distributed with this source code.
// spell-checker:ignore (vars) zline zlines
//! Iterate over lines, including the line ending character(s).
//!
//! This module provides the [`lines`] and [`zlines`] functions,
//! similar to the [`BufRead::lines`] method. While the
//! [`BufRead::lines`] method yields [`String`] instances that do not
//! include the line ending characters (`"\n"` or `"\r\n"`), our
//! functions yield [`String`] instances that include the line ending
//! characters. This is useful if the input data does not end with a
//! newline character and you want to preserve the exact form of the
//! input data.
use std::io::BufRead;
/// The zero byte, representing the null character.
const ZERO: u8 = 0;
/// Returns an iterator over the lines, including line ending characters.
///
/// This function is just like [`BufRead::lines`], but it includes the
/// line ending characters in each yielded [`String`] if the input
/// data has them.
///
/// # Examples
///
/// If the input data does not end with a newline character (`'\n'`),
/// then the last [`String`] yielded by this iterator also does not
/// end with a newline:
///
/// ```rust,ignore
/// use std::io::BufRead;
/// use std::io::Cursor;
///
/// let cursor = Cursor::new(b"x\ny\nz");
/// let mut it = cursor.lines();
///
/// assert_eq!(it.next(), Some(String::from("x\n")));
/// assert_eq!(it.next(), Some(String::from("y\n")));
/// assert_eq!(it.next(), Some(String::from("z")));
/// assert_eq!(it.next(), None);
/// ```
pub(crate) fn lines<B>(reader: B) -> Lines<B>
where
B: BufRead,
{
Lines { buf: reader }
}
/// An iterator over the lines of an instance of `BufRead`.
///
/// This struct is generally created by calling [`lines`] on a `BufRead`.
/// Please see the documentation of [`lines`] for more details.
pub(crate) struct Lines<B> {
buf: B,
}
impl<B: BufRead> Iterator for Lines<B> {
type Item = std::io::Result<String>;
fn next(&mut self) -> Option<std::io::Result<String>> {
let mut buf = String::new();
match self.buf.read_line(&mut buf) {
Ok(0) => None,
Ok(_n) => Some(Ok(buf)),
Err(e) => Some(Err(e)),
}
}
}
/// Returns an iterator over the lines of the given reader.
///
/// The iterator returned from this function will yield instances of
/// [`std::io::Result`]<[`Vec`]<[`u8`]>>, representing the bytes of the line
/// *including* the null character (with the possible exception of the
/// last line, which may not have one).
///
/// # Examples
///
/// ```rust,ignore
/// use std::io::Cursor;
///
/// let cursor = Cursor::new(b"x\0y\0z\0");
/// let mut iter = zlines(cursor).map(|l| l.unwrap());
/// assert_eq!(iter.next(), Some(b"x\0".to_vec()));
/// assert_eq!(iter.next(), Some(b"y\0".to_vec()));
/// assert_eq!(iter.next(), Some(b"z\0".to_vec()));
/// assert_eq!(iter.next(), None);
/// ```
pub fn zlines<B>(buf: B) -> ZLines<B> {
ZLines { buf }
}
/// An iterator over the zero-terminated lines of an instance of `BufRead`.
pub struct ZLines<B> {
buf: B,
}
impl<B: BufRead> Iterator for ZLines<B> {
type Item = std::io::Result<Vec<u8>>;
fn next(&mut self) -> Option<std::io::Result<Vec<u8>>> {
let mut buf = Vec::new();
match self.buf.read_until(ZERO, &mut buf) {
Ok(0) => None,
Ok(_) => Some(Ok(buf)),
Err(e) => Some(Err(e)),
}
}
}
#[cfg(test)]
mod tests {
use crate::lines::lines;
use crate::lines::zlines;
use std::io::Cursor;
#[test]
fn test_null_terminated() {
let cursor = Cursor::new(b"x\0y\0z\0");
let mut iter = zlines(cursor).map(|l| l.unwrap());
assert_eq!(iter.next(), Some(b"x\0".to_vec()));
assert_eq!(iter.next(), Some(b"y\0".to_vec()));
assert_eq!(iter.next(), Some(b"z\0".to_vec()));
assert_eq!(iter.next(), None);
}
#[test]
fn test_not_null_terminated() {
let cursor = Cursor::new(b"x\0y\0z");
let mut iter = zlines(cursor).map(|l| l.unwrap());
assert_eq!(iter.next(), Some(b"x\0".to_vec()));
assert_eq!(iter.next(), Some(b"y\0".to_vec()));
assert_eq!(iter.next(), Some(b"z".to_vec()));
assert_eq!(iter.next(), None);
}
#[test]
fn test_lines() {
let cursor = Cursor::new(b"x\ny\nz");
let mut it = lines(cursor).map(|l| l.unwrap());
assert_eq!(it.next(), Some(String::from("x\n")));
assert_eq!(it.next(), Some(String::from("y\n")));
assert_eq!(it.next(), Some(String::from("z")));
assert_eq!(it.next(), None);
}
}

View file

@ -17,7 +17,7 @@ path = "src/tail.rs"
[dependencies]
clap = { version = "3.0", features = ["wrap_help", "cargo"] }
libc = "0.2.42"
uucore = { version=">=0.0.11", package="uucore", path="../../uucore", features=["ringbuffer"] }
uucore = { version=">=0.0.11", package="uucore", path="../../uucore", features=["ringbuffer", "lines"] }
[target.'cfg(windows)'.dependencies]
winapi = { version="0.3", features=["fileapi", "handleapi", "processthreadsapi", "synchapi", "winbase"] }

View file

@ -16,11 +16,9 @@ extern crate clap;
extern crate uucore;
mod chunks;
mod lines;
mod parse;
mod platform;
use chunks::ReverseChunks;
use lines::lines;
use clap::{App, AppSettings, Arg};
use std::collections::VecDeque;
@ -33,6 +31,7 @@ use std::thread::sleep;
use std::time::Duration;
use uucore::display::Quotable;
use uucore::error::{FromIo, UResult, USimpleError};
use uucore::lines::lines;
use uucore::parse_size::{parse_size, ParseSizeError};
use uucore::ringbuffer::RingBuffer;

View file

@ -55,6 +55,7 @@ encoding = ["data-encoding", "data-encoding-macro", "z85", "thiserror"]
entries = ["libc"]
fs = ["libc", "nix", "winapi-util"]
fsext = ["libc", "time"]
lines = []
memo = ["itertools"]
mode = ["libc"]
perms = ["libc", "walkdir"]

View file

@ -6,6 +6,8 @@ pub mod encoding;
pub mod fs;
#[cfg(feature = "fsext")]
pub mod fsext;
#[cfg(feature = "lines")]
pub mod lines;
#[cfg(feature = "memo")]
pub mod memo;
#[cfg(feature = "ringbuffer")]

View file

@ -2,15 +2,17 @@
// *
// * For the full copyright and license information, please view the LICENSE
// * file that was distributed with this source code.
// spell-checker:ignore (vars)
//! Iterate over lines, including the line ending character(s).
//!
//! This module provides the [`lines`] function, similar to the
//! [`BufRead::lines`] method. While the [`BufRead::lines`] method
//! yields [`String`] instances that do not include the line ending
//! characters (`"\n"` or `"\r\n"`), our function yields [`String`]
//! instances that include the line ending characters. This is useful
//! if the input data does not end with a newline character and you
//! want to preserve the exact form of the input data.
//! characters (`"\n"` or `"\r\n"`), our functions yield
//! [`Vec`]<['u8']> instances that include the line ending
//! characters. This is useful if the input data does not end with a
//! newline character and you want to preserve the exact form of the
//! input data.
use std::io::BufRead;
/// Returns an iterator over the lines, including line ending characters.
@ -51,7 +53,7 @@ use std::io::BufRead;
/// assert_eq!(it.next(), Some(Vec::from("z")));
/// assert_eq!(it.next(), None);
/// ```
pub(crate) fn lines<B>(reader: B, sep: u8) -> Lines<B>
pub fn lines<B>(reader: B, sep: u8) -> Lines<B>
where
B: BufRead,
{
@ -62,7 +64,7 @@ where
///
/// This struct is generally created by calling [`lines`] on a `BufRead`.
/// Please see the documentation of [`lines`] for more details.
pub(crate) struct Lines<B> {
pub struct Lines<B> {
buf: B,
sep: u8,
}

View file

@ -38,6 +38,8 @@ pub use crate::features::encoding;
pub use crate::features::fs;
#[cfg(feature = "fsext")]
pub use crate::features::fsext;
#[cfg(feature = "lines")]
pub use crate::features::lines;
#[cfg(feature = "memo")]
pub use crate::features::memo;
#[cfg(feature = "ringbuffer")]