Skip to content

Commit 4467116

Browse files
Adjust paste implementation per code review
1 parent cf557b3 commit 4467116

File tree

1 file changed

+30
-34
lines changed

1 file changed

+30
-34
lines changed

text/paste.rs

Lines changed: 30 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,9 @@
66
// file in the root directory of this project.
77
// SPDX-License-Identifier: MIT
88
//
9+
910
// TODO:
10-
// - stdin ("-")
11-
// -- Fixed
12-
// - fix: empty-string delimiters \0
13-
// -- Fixed
1411
// - improve: don't open all files at once in --serial mode
15-
//
1612

1713
use clap::Parser;
1814
use gettextrs::{bind_textdomain_codeset, setlocale, textdomain, LocaleCategory};
@@ -34,9 +30,9 @@ struct Args {
3430
serial: bool,
3531

3632
/// Delimiter list
37-
// Other implementations use "delimiters"
38-
#[arg(alias = "delimiters", short, long)]
39-
delims: Option<String>,
33+
// Other implementations use "delimiters" as the long form, so mirror that
34+
#[arg(short, long)]
35+
delimiters: Option<String>,
4036

4137
/// One or more input files
4238
files: Vec<String>,
@@ -95,7 +91,7 @@ struct PasteInfo {
9591
pub inputs: Vec<PasteFile>,
9692
}
9793

98-
enum DelimsInfo<'a> {
94+
enum DelimiterState<'a> {
9995
NoDelimiters,
10096
SingleDelimiter(&'a [u8]),
10197
MultipleDelimiters {
@@ -104,9 +100,9 @@ enum DelimsInfo<'a> {
104100
},
105101
}
106102

107-
impl<'a> DelimsInfo<'a> {
108-
fn new(parsed_delims_argument_ref: &'a [Box<[u8]>]) -> DelimsInfo<'a> {
109-
match parsed_delims_argument_ref {
103+
impl<'a> DelimiterState<'a> {
104+
fn new(parsed_delimiters_argument_ref: &'a [Box<[u8]>]) -> DelimiterState<'a> {
105+
match parsed_delimiters_argument_ref {
110106
[] => Self::NoDelimiters,
111107
[only_delimiter] => {
112108
// -d '\0' has the same effect as -d ''
@@ -117,18 +113,18 @@ impl<'a> DelimsInfo<'a> {
117113
}
118114
}
119115
_ => Self::MultipleDelimiters {
120-
delimiters: parsed_delims_argument_ref,
121-
delimiters_iterator: parsed_delims_argument_ref.iter().cycle(),
116+
delimiters: parsed_delimiters_argument_ref,
117+
delimiters_iterator: parsed_delimiters_argument_ref.iter().cycle(),
122118
},
123119
}
124120
}
125121

126122
fn write(&mut self, write: &mut impl io::Write) -> io::Result<()> {
127123
match *self {
128-
DelimsInfo::SingleDelimiter(sl) => {
124+
DelimiterState::SingleDelimiter(sl) => {
129125
write.write_all(sl)?;
130126
}
131-
DelimsInfo::MultipleDelimiters {
127+
DelimiterState::MultipleDelimiters {
132128
ref mut delimiters_iterator,
133129
..
134130
} => {
@@ -145,7 +141,7 @@ impl<'a> DelimsInfo<'a> {
145141

146142
fn reset(&mut self) {
147143
match self {
148-
DelimsInfo::MultipleDelimiters {
144+
DelimiterState::MultipleDelimiters {
149145
delimiters,
150146
ref mut delimiters_iterator,
151147
..
@@ -159,7 +155,7 @@ impl<'a> DelimsInfo<'a> {
159155
}
160156
}
161157

162-
/// `delims`: Delimiters parsed from "-d"/"--delims" argument
158+
/// `delimiters`: Delimiters parsed from "-d"/"--delimiters" argument
163159
// Support for empty delimiter list:
164160
//
165161
// bsdutils: no, supports "-d" but requires the delimiter list to be non-empty
@@ -171,14 +167,14 @@ impl<'a> DelimsInfo<'a> {
171167
// POSIX seems to almost forbid this:
172168
// "These elements specify one or more delimiters to use, instead of the default <tab>, to replace the <newline> of the input lines."
173169
// https://pubs.opengroup.org/onlinepubs/9799919799/utilities/paste.html
174-
fn parse_delims_argument(delims: Option<String>) -> Result<Box<[Box<[u8]>]>, String> {
170+
fn parse_delimiters_argument(delimiters: Option<String>) -> Result<Box<[Box<[u8]>]>, String> {
175171
const BACKSLASH: char = '\\';
176172

177173
fn add_normal_delimiter(ve: &mut Vec<Box<[u8]>>, byte: u8) {
178174
ve.push(Box::new([byte]));
179175
}
180176

181-
let Some(delims_string) = delims else {
177+
let Some(delimiters_string) = delimiters else {
182178
// Default when no delimiter argument is provided
183179
return Ok(Box::new([Box::new([b'\t'])]));
184180
};
@@ -191,9 +187,9 @@ fn parse_delims_argument(delims: Option<String>) -> Result<Box<[Box<[u8]>]>, Str
191187
ve.push(Box::from(encoded.as_bytes()));
192188
};
193189

194-
let mut vec = Vec::<Box<[u8]>>::with_capacity(delims_string.len());
190+
let mut vec = Vec::<Box<[u8]>>::with_capacity(delimiters_string.len());
195191

196-
let mut chars = delims_string.chars();
192+
let mut chars = delimiters_string.chars();
197193

198194
while let Some(char) = chars.next() {
199195
match char {
@@ -223,7 +219,7 @@ fn parse_delims_argument(delims: Option<String>) -> Result<Box<[Box<[u8]>]>, Str
223219
}
224220
None => {
225221
return Err(format!(
226-
"delimiter list ends with an unescaped backslash: {delims_string}"
222+
"delimiter list ends with an unescaped backslash: {delimiters_string}"
227223
));
228224
}
229225
},
@@ -295,7 +291,7 @@ fn open_inputs(files: Vec<String>) -> Result<PasteInfo, Box<dyn Error>> {
295291

296292
fn paste_files_serial(
297293
mut paste_info: PasteInfo,
298-
mut delims_info: DelimsInfo,
294+
mut delimiter_state: DelimiterState,
299295
) -> Result<(), Box<dyn Error>> {
300296
let mut stdout_lock = io::stdout().lock();
301297

@@ -320,7 +316,7 @@ fn paste_files_serial(
320316
break;
321317
} else {
322318
if !first_line {
323-
delims_info.write(&mut stdout_lock)?;
319+
delimiter_state.write(&mut stdout_lock)?;
324320
}
325321

326322
// output line segment
@@ -351,15 +347,15 @@ fn paste_files_serial(
351347
// When the -s option is specified:
352348
// The last <newline> in a file shall not be modified.
353349
// The delimiter shall be reset to the first element of list after each file operand is processed.
354-
delims_info.reset();
350+
delimiter_state.reset();
355351
}
356352

357353
Ok(())
358354
}
359355

360356
fn paste_files(
361357
mut paste_info: PasteInfo,
362-
mut delims_info: DelimsInfo,
358+
mut delimiter_state: DelimiterState,
363359
) -> Result<(), Box<dyn Error>> {
364360
// for each input line, across N files
365361

@@ -406,7 +402,7 @@ fn paste_files(
406402
output.push(b'\n');
407403
} else {
408404
// next delimiter
409-
delims_info.write(&mut output)?;
405+
delimiter_state.write(&mut output)?;
410406
}
411407
}
412408

@@ -417,7 +413,7 @@ fn paste_files(
417413
// output all segments to stdout at once (one write per line)
418414
io::stdout().write_all(output.as_slice())?;
419415

420-
delims_info.reset();
416+
delimiter_state.reset();
421417
}
422418

423419
Ok(())
@@ -432,12 +428,12 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
432428
bind_textdomain_codeset(PROJECT_NAME, "UTF-8")?;
433429

434430
let Args {
435-
delims,
431+
delimiters,
436432
files,
437433
serial,
438434
} = args;
439435

440-
let parsed_delims_argument = match parse_delims_argument(delims) {
436+
let parsed_delimiters_argument = match parse_delimiters_argument(delimiters) {
441437
Ok(bo) => bo,
442438
Err(st) => {
443439
eprintln!("paste: {st}");
@@ -459,12 +455,12 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
459455
}
460456
};
461457

462-
let delims_info = DelimsInfo::new(&parsed_delims_argument);
458+
let delimiter_state = DelimiterState::new(&parsed_delimiters_argument);
463459

464460
if serial {
465-
paste_files_serial(paste_info, delims_info)?;
461+
paste_files_serial(paste_info, delimiter_state)?;
466462
} else {
467-
paste_files(paste_info, delims_info)?;
463+
paste_files(paste_info, delimiter_state)?;
468464
}
469465

470466
Ok(())

0 commit comments

Comments
 (0)