Skip to content

Commit c42569e

Browse files
committed
sleep: use uucore duration parsing instead of fundu
1 parent 6adf07e commit c42569e

File tree

5 files changed

+47
-41
lines changed

5 files changed

+47
-41
lines changed

Cargo.lock

Lines changed: 7 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/uu/sleep/src/sleep.rs

Lines changed: 3 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -61,37 +61,17 @@ pub fn uu_app() -> Command {
6161
fn sleep(args: &[&str]) -> UResult<()> {
6262
let mut arg_error = false;
6363

64-
use fundu::TimeUnit::{Day, Hour, Minute, Second};
65-
let parser = DurationParser::with_time_units(&[Second, Minute, Hour, Day]);
66-
6764
let sleep_dur = args
6865
.iter()
69-
.filter_map(|input| match parser.parse(input.trim()) {
66+
.filter_map(|input| match uucore::parse_time::from_str(input.trim()) {
7067
Ok(duration) => Some(duration),
7168
Err(error) => {
7269
arg_error = true;
73-
74-
let reason = match error {
75-
ParseError::Empty if input.is_empty() => "Input was empty".to_string(),
76-
ParseError::Empty => "Found only whitespace in input".to_string(),
77-
ParseError::Syntax(pos, description)
78-
| ParseError::TimeUnit(pos, description) => {
79-
format!("{description} at position {}", pos.saturating_add(1))
80-
}
81-
ParseError::NegativeExponentOverflow | ParseError::PositiveExponentOverflow => {
82-
"Exponent was out of bounds".to_string()
83-
}
84-
ParseError::NegativeNumber => "Number was negative".to_string(),
85-
error => error.to_string(),
86-
};
87-
show_error!("invalid time interval '{input}': {reason}");
88-
70+
show_error!("{error}");
8971
None
9072
}
9173
})
92-
.fold(Duration::ZERO, |acc, n| {
93-
acc.saturating_add(SaturatingInto::<std::time::Duration>::saturating_into(n))
94-
});
74+
.fold(Duration::ZERO, |acc, n| acc.saturating_add(n));
9575

9676
if arg_error {
9777
return Err(UUsageError::new(1, ""));

src/uucore/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ sha3 = { workspace = true, optional = true }
5151
blake2b_simd = { workspace = true, optional = true }
5252
blake3 = { workspace = true, optional = true }
5353
sm3 = { workspace = true, optional = true }
54+
hexfloat2 = "0.1.3"
5455

5556
[target.'cfg(unix)'.dependencies]
5657
walkdir = { workspace = true, optional = true }

src/uucore/src/lib/parser/parse_time.rs

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ use std::time::Duration;
1212

1313
use crate::display::Quotable;
1414

15+
use hexfloat2::parse;
16+
1517
/// Parse a duration from a string.
1618
///
1719
/// The string may contain only a number, like "123" or "4.5", or it
@@ -49,16 +51,21 @@ pub fn from_str(string: &str) -> Result<Duration, String> {
4951
if len == 0 {
5052
return Err("empty string".to_owned());
5153
}
54+
let base = match string.get(0..2) {
55+
Some("0x") => 16,
56+
_ => 10,
57+
};
5258
let slice = match string.get(..len - 1) {
5359
Some(s) => s,
5460
None => return Err(format!("invalid time interval {}", string.quote())),
5561
};
56-
let (numstr, times) = match string.chars().next_back().unwrap() {
57-
's' => (slice, 1),
58-
'm' => (slice, 60),
59-
'h' => (slice, 60 * 60),
60-
'd' => (slice, 60 * 60 * 24),
61-
val if !val.is_alphabetic() => (string, 1),
62+
let (numstr, times) = match (string.chars().next_back().unwrap(), base) {
63+
('s', _) => (slice, 1),
64+
('m', _) => (slice, 60),
65+
('h', _) => (slice, 60 * 60),
66+
// if the number is hex, GNU coreutils will parse the trailing d as part of the number, not as days
67+
('d', 10) => (slice, 60 * 60 * 24),
68+
(val, _) if !val.is_alphabetic() => (string, 1),
6269
_ => {
6370
if string == "inf" || string == "infinity" {
6471
("inf", 1)
@@ -67,9 +74,14 @@ pub fn from_str(string: &str) -> Result<Duration, String> {
6774
}
6875
}
6976
};
70-
let num = numstr
71-
.parse::<f64>()
72-
.map_err(|e| format!("invalid time interval {}: {}", string.quote(), e))?;
77+
let num = if base == 16 {
78+
// hexfloat2 ParseErrors are ZSTs
79+
parse::<f64>(numstr).map_err(|_| format!("invalid time interval {}", string.quote()))?
80+
} else {
81+
numstr
82+
.parse::<f64>()
83+
.map_err(|e| format!("invalid time interval {}: {}", string.quote(), e))?
84+
};
7385

7486
if num < 0. {
7587
return Err(format!("invalid time interval {}", string.quote()));

tests/by-util/test_sleep.rs

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,11 @@ fn test_invalid_time_interval() {
1414
new_ucmd!()
1515
.arg("xyz")
1616
.fails()
17-
.usage_error("invalid time interval 'xyz': Invalid input: xyz");
17+
.usage_error("invalid time interval 'xyz'");
1818
new_ucmd!()
1919
.args(&["--", "-1"])
2020
.fails()
21-
.usage_error("invalid time interval '-1': Number was negative");
21+
.usage_error("invalid time interval '-1'");
2222
}
2323

2424
#[test]
@@ -208,16 +208,14 @@ fn test_sleep_when_input_has_only_whitespace_then_error(#[case] input: &str) {
208208
.arg(input)
209209
.timeout(Duration::from_secs(10))
210210
.fails()
211-
.usage_error(format!(
212-
"invalid time interval '{input}': Found only whitespace in input"
213-
));
211+
.usage_error("empty string");
214212
}
215213

216214
#[test]
217215
fn test_sleep_when_multiple_input_some_with_error_then_shows_all_errors() {
218-
let expected = "invalid time interval 'abc': Invalid input: abc\n\
219-
sleep: invalid time interval '1years': Invalid time unit: 'years' at position 2\n\
220-
sleep: invalid time interval ' ': Found only whitespace in input";
216+
let expected = "invalid time interval 'abc'\n\
217+
sleep: invalid time interval '1years': invalid float literal\n\
218+
sleep: empty string";
221219

222220
// Even if one of the arguments is valid, but the rest isn't, we should still fail and exit early.
223221
// So, the timeout of 10 seconds ensures we haven't executed `thread::sleep` with the only valid
@@ -234,5 +232,13 @@ fn test_negative_interval() {
234232
new_ucmd!()
235233
.args(&["--", "-1"])
236234
.fails()
237-
.usage_error("invalid time interval '-1': Number was negative");
235+
.usage_error("invalid time interval '-1'");
236+
}
237+
238+
#[rstest]
239+
#[case::int("0x0")]
240+
#[case::frac("0x0.1")]
241+
#[case::scientific("0x1.0p-3")]
242+
fn test_hex_duration(#[case] input: &str) {
243+
new_ucmd!().arg(input).succeeds().no_output();
238244
}

0 commit comments

Comments
 (0)