add the code from rust/coreutils

This commit is contained in:
Sylvestre Ledru
2023-04-23 18:40:49 +02:00
parent e499cb99be
commit 348b4bc214
3 changed files with 271 additions and 0 deletions
Generated
+65
View File
@@ -0,0 +1,65 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "aho-corasick"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "67fc08ce920c31afb70f013dcce1bfc3a3195de6a228474e45e1f145b36f8d04"
dependencies = [
"memchr",
]
[[package]]
name = "humantime_to_duration"
version = "0.1.0"
dependencies = [
"regex",
"time",
]
[[package]]
name = "memchr"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
[[package]]
name = "regex"
version = "1.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "af83e617f331cc6ae2da5443c602dfa5af81e517212d9d611a5b3ba1777b5370"
dependencies = [
"aho-corasick",
"memchr",
"regex-syntax",
]
[[package]]
name = "regex-syntax"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a5996294f19bd3aae0453a862ad728f60e6600695733dd5df01da90c54363a3c"
[[package]]
name = "serde"
version = "1.0.160"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bb2f3770c8bce3bcda7e149193a069a0f4365bda1fa5cd88e03bca26afc1216c"
[[package]]
name = "time"
version = "0.3.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cd0cbfecb4d19b5ea75bb31ad904eb5b9fa13f21079c3b92017ebdf4999a5890"
dependencies = [
"serde",
"time-core",
]
[[package]]
name = "time-core"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2e153e1f1acaef8acc537e68b44906d2db6436e2b35ac2c6b42640fff91f00fd"
+10
View File
@@ -0,0 +1,10 @@
[package]
name = "humantime_to_duration"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
regex = "1.8.1"
time = "0.3.20"
+196
View File
@@ -0,0 +1,196 @@
// For the full copyright and license information, please view the LICENSE
// file that was distributed with this source code.
use regex::Regex;
use time::Duration;
/// Parses a relative time string and returns a `Duration` representing the
/// relative time.
///
/// # Arguments
///
/// * `s` - A string slice representing the relative time.
///
/// # Examples
///
/// ```
/// use time::Duration;
/// let duration = humantime_to_duration::from_str("+3 days");
/// assert_eq!(duration, Some(Duration::days(3)));
/// ```
///
/// # Supported formats
///
/// The function supports the following formats for relative time:
///
/// * `num` `unit` (e.g., "-1 hour", "+3 days")
/// * `unit` (e.g., "hour", "day")
/// * "now" or "today"
/// * "yesterday"
/// * "tomorrow"
/// * use "ago" for the past
///
/// [num] can be a positive or negative integer.
/// [unit] can be one of the following: "fortnight", "week", "day", "hour",
/// "minute", "min", "second", "sec" and their plural forms.
///
/// It is also possible to pass "1 hour 2 minutes" or "2 days and 2 hours"
///
/// # Returns
///
/// * `Some(Duration)` - If the input string can be parsed as a relative time
/// * `None` - If the input string cannot be parsed as a relative time
///
/// # Errors
///
/// This function will return `None` if the input string cannot be parsed as a
/// relative time.
pub fn from_str(s: &str) -> Option<Duration> {
let time_pattern: Regex = Regex::new(
r"(?x)
(?P<value>[-+]?\d*)\s*
(?P<unit>years?|months?|fortnights?|weeks?|days?|hours?|h|minutes?|mins?|m|seconds?|secs?|s|yesterday|tomorrow|now|today)
(\s*(?P<separator>and|,)?\s*)?"
)
.unwrap();
let mut total_duration = Duration::ZERO;
let mut is_ago = s.contains(" ago");
for capture in time_pattern.captures_iter(s) {
let value_str = capture.name("value").unwrap().as_str();
let value = if value_str.is_empty() {
1
} else {
value_str.parse::<i64>().unwrap_or(1)
};
let unit = capture.name("unit").unwrap().as_str();
if capture.name("ago").is_some() {
is_ago = true;
}
let duration = match unit {
"years" | "year" => Duration::days(value * 365),
"months" | "month" => Duration::days(value * 30),
"fortnights" | "fortnight" => Duration::weeks(value * 2),
"weeks" | "week" => Duration::weeks(value),
"days" | "day" => Duration::days(value),
"hours" | "hour" | "h" => Duration::hours(value),
"minutes" | "minute" | "mins" | "min" | "m" => Duration::minutes(value),
"seconds" | "second" | "secs" | "sec" | "s" => Duration::seconds(value),
"yesterday" => Duration::days(-1),
"tomorrow" => Duration::days(1),
"now" | "today" => Duration::ZERO,
_ => return None,
};
total_duration = total_duration.checked_add(if is_ago { -duration } else { duration })?;
}
if total_duration == Duration::ZERO && !time_pattern.is_match(s) {
None
} else {
Some(total_duration)
}
}
#[cfg(test)]
mod tests {
use super::from_str;
use time::Duration;
#[test]
fn test_years() {
assert_eq!(from_str("1 year"), Some(Duration::seconds(31536000)));
assert_eq!(from_str("-2 years"), Some(Duration::seconds(-63072000)));
assert_eq!(from_str("2 years ago"), Some(Duration::seconds(-63072000)));
assert_eq!(from_str("year"), Some(Duration::seconds(31536000)));
}
#[test]
fn test_months() {
assert_eq!(from_str("1 month"), Some(Duration::seconds(2592000)));
assert_eq!(from_str("1 month and 2 weeks"), Some(Duration::seconds(3801600)));
assert_eq!(from_str("1 month and 2 weeks ago"), Some(Duration::seconds(-3801600)));
assert_eq!(from_str("2 months"), Some(Duration::seconds(5184000)));
assert_eq!(from_str("month"), Some(Duration::seconds(2592000)));
}
#[test]
fn test_fortnights() {
assert_eq!(from_str("1 fortnight"), Some(Duration::seconds(1209600)));
assert_eq!(from_str("3 fortnights"), Some(Duration::seconds(3628800)));
assert_eq!(from_str("fortnight"), Some(Duration::seconds(1209600)));
}
#[test]
fn test_weeks() {
assert_eq!(from_str("1 week"), Some(Duration::seconds(604800)));
assert_eq!(from_str("1 week 3 days"), Some(Duration::seconds(864000)));
assert_eq!(from_str("1 week 3 days ago"), Some(Duration::seconds(-864000)));
assert_eq!(from_str("-2 weeks"), Some(Duration::seconds(-1209600)));
assert_eq!(from_str("2 weeks ago"), Some(Duration::seconds(-1209600)));
assert_eq!(from_str("week"), Some(Duration::seconds(604800)));
}
#[test]
fn test_days() {
assert_eq!(from_str("1 day"), Some(Duration::seconds(86400)));
assert_eq!(from_str("2 days ago"), Some(Duration::seconds(-172800)));
assert_eq!(from_str("-2 days"), Some(Duration::seconds(-172800)));
assert_eq!(from_str("day"), Some(Duration::seconds(86400)));
}
#[test]
fn test_hours() {
assert_eq!(from_str("1 hour"), Some(Duration::seconds(3600)));
assert_eq!(from_str("1 hour ago"), Some(Duration::seconds(-3600)));
assert_eq!(from_str("-2 hours"), Some(Duration::seconds(-7200)));
assert_eq!(from_str("hour"), Some(Duration::seconds(3600)));
}
#[test]
fn test_minutes() {
assert_eq!(from_str("1 minute"), Some(Duration::seconds(60)));
assert_eq!(from_str("2 minutes"), Some(Duration::seconds(120)));
assert_eq!(from_str("min"), Some(Duration::seconds(60)));
}
#[test]
fn test_seconds() {
assert_eq!(from_str("1 second"), Some(Duration::seconds(1)));
assert_eq!(from_str("2 seconds"), Some(Duration::seconds(2)));
assert_eq!(from_str("sec"), Some(Duration::seconds(1)));
}
#[test]
fn test_relative_days() {
assert_eq!(from_str("now"), Some(Duration::seconds(0)));
assert_eq!(from_str("today"), Some(Duration::seconds(0)));
assert_eq!(from_str("yesterday"), Some(Duration::seconds(-86400)));
assert_eq!(from_str("tomorrow"), Some(Duration::seconds(86400)));
}
#[test]
fn test_no_spaces() {
assert_eq!(from_str("-1hour"), Some(Duration::hours(-1)));
assert_eq!(from_str("+3days"), Some(Duration::days(3)));
assert_eq!(from_str("2weeks"), Some(Duration::weeks(2)));
assert_eq!(from_str("2weeks 1hour"), Some(Duration::seconds(1213200)));
assert_eq!(from_str("2weeks 1hour ago"), Some(Duration::seconds(-1213200)));
assert_eq!(from_str("+4months"), Some(Duration::days(4 * 30)));
assert_eq!(from_str("-2years"), Some(Duration::days(-2 * 365)));
assert_eq!(from_str("15minutes"), Some(Duration::minutes(15)));
assert_eq!(from_str("-30seconds"), Some(Duration::seconds(-30)));
assert_eq!(from_str("30seconds ago"), Some(Duration::seconds(-30)));
}
#[test]
fn test_invalid_input() {
assert_eq!(from_str("invalid"), None);
assert_eq!(from_str("1 invalid"), None);
}
}