Skip to content

Commit f197895

Browse files
Merge pull request #5156 from cakebaker/nl_implement_try_from_for_numbering_style
nl: implement TryFrom<&str> for NumberingStyle
2 parents 7bc3c5d + d5ab7bb commit f197895

File tree

3 files changed

+77
-56
lines changed

3 files changed

+77
-56
lines changed

src/uu/nl/src/helper.rs

Lines changed: 21 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -2,25 +2,6 @@
22

33
use crate::options;
44

5-
// parse_style parses a style string into a NumberingStyle.
6-
fn parse_style(chars: &[char]) -> Result<crate::NumberingStyle, String> {
7-
if chars.len() == 1 && chars[0] == 'a' {
8-
Ok(crate::NumberingStyle::All)
9-
} else if chars.len() == 1 && chars[0] == 't' {
10-
Ok(crate::NumberingStyle::NonEmpty)
11-
} else if chars.len() == 1 && chars[0] == 'n' {
12-
Ok(crate::NumberingStyle::None)
13-
} else if chars.len() > 1 && chars[0] == 'p' {
14-
let s: String = chars[1..].iter().cloned().collect();
15-
match regex::Regex::new(&s) {
16-
Ok(re) => Ok(crate::NumberingStyle::Regex(Box::new(re))),
17-
Err(_) => Err(String::from("Illegal regular expression")),
18-
}
19-
} else {
20-
Err(String::from("Illegal style encountered"))
21-
}
22-
}
23-
245
// parse_options loads the options into the settings, returning an array of
256
// error messages.
267
#[allow(clippy::cognitive_complexity)]
@@ -35,47 +16,32 @@ pub fn parse_options(settings: &mut crate::Settings, opts: &clap::ArgMatches) ->
3516
.get_one::<String>(options::NUMBER_FORMAT)
3617
.map(Into::into)
3718
.unwrap_or_default();
38-
match opts.get_one::<String>(options::BODY_NUMBERING) {
19+
match opts
20+
.get_one::<String>(options::HEADER_NUMBERING)
21+
.map(String::as_str)
22+
.map(TryInto::try_into)
23+
{
3924
None => {}
40-
Some(val) => {
41-
let chars: Vec<char> = val.chars().collect();
42-
match parse_style(&chars) {
43-
Ok(s) => {
44-
settings.body_numbering = s;
45-
}
46-
Err(message) => {
47-
errs.push(message);
48-
}
49-
}
50-
}
25+
Some(Ok(style)) => settings.header_numbering = style,
26+
Some(Err(message)) => errs.push(message.to_string()),
5127
}
52-
match opts.get_one::<String>(options::FOOTER_NUMBERING) {
28+
match opts
29+
.get_one::<String>(options::BODY_NUMBERING)
30+
.map(String::as_str)
31+
.map(TryInto::try_into)
32+
{
5333
None => {}
54-
Some(val) => {
55-
let chars: Vec<char> = val.chars().collect();
56-
match parse_style(&chars) {
57-
Ok(s) => {
58-
settings.footer_numbering = s;
59-
}
60-
Err(message) => {
61-
errs.push(message);
62-
}
63-
}
64-
}
34+
Some(Ok(style)) => settings.body_numbering = style,
35+
Some(Err(message)) => errs.push(message.to_string()),
6536
}
66-
match opts.get_one::<String>(options::HEADER_NUMBERING) {
37+
match opts
38+
.get_one::<String>(options::FOOTER_NUMBERING)
39+
.map(String::as_str)
40+
.map(TryInto::try_into)
41+
{
6742
None => {}
68-
Some(val) => {
69-
let chars: Vec<char> = val.chars().collect();
70-
match parse_style(&chars) {
71-
Ok(s) => {
72-
settings.header_numbering = s;
73-
}
74-
Err(message) => {
75-
errs.push(message);
76-
}
77-
}
78-
}
43+
Some(Ok(style)) => settings.footer_numbering = style,
44+
Some(Err(message)) => errs.push(message.to_string()),
7945
}
8046
match opts.get_one::<usize>(options::NUMBER_WIDTH) {
8147
None => {}

src/uu/nl/src/nl.rs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,23 @@ enum NumberingStyle {
7171
Regex(Box<regex::Regex>),
7272
}
7373

74+
impl TryFrom<&str> for NumberingStyle {
75+
type Error = String;
76+
77+
fn try_from(s: &str) -> Result<Self, Self::Error> {
78+
match s {
79+
"a" => Ok(Self::All),
80+
"t" => Ok(Self::NonEmpty),
81+
"n" => Ok(Self::None),
82+
_ if s.starts_with('p') => match regex::Regex::new(&s[1..]) {
83+
Ok(re) => Ok(Self::Regex(Box::new(re))),
84+
Err(_) => Err(String::from("invalid regular expression")),
85+
},
86+
_ => Err(format!("invalid numbering style: '{s}'")),
87+
}
88+
}
89+
}
90+
7491
// NumberFormat specifies how line numbers are output within their allocated
7592
// space. They are justified to the left or right, in the latter case with
7693
// the option of having all unused space to its left turned into leading zeroes.

tests/by-util/test_nl.rs

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// spell-checker:ignore iinvalid linvalid ninvalid vinvalid winvalid
1+
// spell-checker:ignore binvalid finvalid hinvalid iinvalid linvalid ninvalid vinvalid winvalid
22
use crate::common::util::TestScenario;
33

44
#[test]
@@ -426,3 +426,41 @@ fn test_numbering_matched_lines() {
426426
}
427427
}
428428
}
429+
430+
#[test]
431+
fn test_invalid_numbering() {
432+
let invalid_args = [
433+
"-hinvalid",
434+
"--header-numbering=invalid",
435+
"-binvalid",
436+
"--body-numbering=invalid",
437+
"-finvalid",
438+
"--footer-numbering=invalid",
439+
];
440+
441+
for invalid_arg in invalid_args {
442+
new_ucmd!()
443+
.arg(invalid_arg)
444+
.fails()
445+
.stderr_contains("invalid numbering style: 'invalid'");
446+
}
447+
}
448+
449+
#[test]
450+
fn test_invalid_regex_numbering() {
451+
let invalid_args = [
452+
"-hp[",
453+
"--header-numbering=p[",
454+
"-bp[",
455+
"--body-numbering=p[",
456+
"-fp[",
457+
"--footer-numbering=p[",
458+
];
459+
460+
for invalid_arg in invalid_args {
461+
new_ucmd!()
462+
.arg(invalid_arg)
463+
.fails()
464+
.stderr_contains("invalid regular expression");
465+
}
466+
}

0 commit comments

Comments
 (0)