mirror of
https://github.com/openharmony/third_party_rust_humantime.git
synced 2026-06-30 21:17:57 -04:00
Initial implementation
This commit is contained in:
@@ -0,0 +1,3 @@
|
||||
/Cargo.lock
|
||||
/.vagga
|
||||
/target
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
[package]
|
||||
name = "humantime"
|
||||
description = """
|
||||
A human friendly format parser for std::time::Duration.
|
||||
"""
|
||||
license = "MIT/Apache-2.0"
|
||||
readme = "README.rst"
|
||||
keywords = ["linux", "container", "namespace", "docker", "process"]
|
||||
homepage = "http://github.com/tailhook/humantime"
|
||||
documentation = "http://tailhook.github.io/humantime/"
|
||||
version = "0.1.0"
|
||||
authors = ["paul@colomiets.name"]
|
||||
|
||||
[dependencies]
|
||||
quick-error = "1.0.0"
|
||||
|
||||
[dev-dependencies]
|
||||
argparse = "0.2.1"
|
||||
|
||||
[lib]
|
||||
name = "humantime"
|
||||
path = "src/lib.rs"
|
||||
|
||||
+242
@@ -0,0 +1,242 @@
|
||||
use std::str::Chars;
|
||||
use std::time::Duration;
|
||||
use std::error::Error as StdError;
|
||||
|
||||
quick_error! {
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum Error {
|
||||
InvalidCharacter(offset: usize) {
|
||||
display("invalid character at {}", offset)
|
||||
description("invalid character")
|
||||
}
|
||||
NumberExpected(offset: usize) {
|
||||
display("expected number at {}", offset)
|
||||
description("expected number")
|
||||
}
|
||||
UnknownUnit(start: usize, end: usize) {
|
||||
display("unknown unit at {}-{}", start, end)
|
||||
description("unknown unit")
|
||||
}
|
||||
NumberOverflow {
|
||||
display(self_) -> ("{}", self_.description())
|
||||
description("number is too large")
|
||||
}
|
||||
Empty {
|
||||
display(self_) -> ("{}", self_.description())
|
||||
description("value was empty")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
trait OverflowOp: Sized {
|
||||
fn mul(self, other: Self) -> Result<Self, Error>;
|
||||
fn add(self, other: Self) -> Result<Self, Error>;
|
||||
}
|
||||
|
||||
impl OverflowOp for u64 {
|
||||
fn mul(self, other: Self) -> Result<Self, Error> {
|
||||
self.checked_mul(other).ok_or(Error::NumberOverflow)
|
||||
}
|
||||
fn add(self, other: Self) -> Result<Self, Error> {
|
||||
self.checked_add(other).ok_or(Error::NumberOverflow)
|
||||
}
|
||||
}
|
||||
|
||||
struct Parser<'a> {
|
||||
iter: Chars<'a>,
|
||||
src: &'a str,
|
||||
current: (u64, u64),
|
||||
}
|
||||
|
||||
impl<'a> Parser<'a> {
|
||||
fn off(&self) -> usize {
|
||||
self.src.len() - self.iter.as_str().len()
|
||||
}
|
||||
|
||||
fn parse_first_char(&mut self) -> Result<Option<u64>, Error> {
|
||||
let off = self.off();
|
||||
for c in self.iter.by_ref() {
|
||||
match c {
|
||||
'0'...'9' => {
|
||||
return Ok(Some(c as u64 - '0' as u64));
|
||||
}
|
||||
c if c.is_whitespace() => continue,
|
||||
_ => {
|
||||
return Err(Error::NumberExpected(off));
|
||||
}
|
||||
}
|
||||
}
|
||||
return Ok(None);
|
||||
}
|
||||
fn parse_unit(&mut self, n: u64, start: usize, end: usize)
|
||||
-> Result<(), Error>
|
||||
{
|
||||
let (mut sec, nsec) = match &self.src[start..end] {
|
||||
"nsec" | "ns" => (0u64, n),
|
||||
"usec" | "us" => (0u64, try!(n.mul(1000))),
|
||||
"msec" | "ms" => (0u64, try!(n.mul(1000_000))),
|
||||
"seconds" | "second" | "sec" | "s" => (n, 0),
|
||||
"minutes" | "minute" | "min" | "m" => (try!(n.mul(60)), 0),
|
||||
"hours" | "hour" | "hr" | "h" => (try!(n.mul(3600)), 0),
|
||||
"days" | "day" | "d" => (try!(n.mul(86400)), 0),
|
||||
"weeks" | "week" | "w" => (try!(n.mul(86400*7)), 0),
|
||||
"months" | "month" | "M" => (try!(n.mul(2630016)), 0), // 30.44d
|
||||
"years" | "year" | "y" => (try!(n.mul(31557600)), 0), // 365.25d
|
||||
_ => return Err(Error::UnknownUnit(start, end)),
|
||||
};
|
||||
let mut nsec = try!(self.current.1.add(nsec));
|
||||
if nsec > 1000_000_000 {
|
||||
sec = try!(sec.add(nsec / 1000_000_000));
|
||||
nsec %= 1000_000_000;
|
||||
}
|
||||
sec = try!(self.current.0.add(sec));
|
||||
self.current = (sec, nsec);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn parse(mut self) -> Result<Duration, Error> {
|
||||
let mut n = try!(try!(self.parse_first_char()).ok_or(Error::Empty));
|
||||
'outer: loop {
|
||||
let mut off = self.off();
|
||||
while let Some(c) = self.iter.next() {
|
||||
match c {
|
||||
'0'...'9' => {
|
||||
n = try!(n.checked_mul(10)
|
||||
.and_then(|x| x.checked_add(c as u64 - '0' as u64))
|
||||
.ok_or(Error::NumberOverflow));
|
||||
}
|
||||
c if c.is_whitespace() => {}
|
||||
'a'...'z' | 'A'...'Z' => {
|
||||
break;
|
||||
}
|
||||
_ => {
|
||||
return Err(Error::InvalidCharacter(off));
|
||||
}
|
||||
}
|
||||
off = self.off();
|
||||
}
|
||||
let start = off;
|
||||
let mut off = self.off();
|
||||
while let Some(c) = self.iter.next() {
|
||||
match c {
|
||||
'0'...'9' => {
|
||||
try!(self.parse_unit(n, start, off));
|
||||
n = c as u64 - '0' as u64;
|
||||
continue 'outer;
|
||||
}
|
||||
c if c.is_whitespace() => break,
|
||||
'a'...'z' | 'A'...'Z' => {}
|
||||
_ => {
|
||||
return Err(Error::InvalidCharacter(off));
|
||||
}
|
||||
}
|
||||
off = self.off();
|
||||
}
|
||||
try!(self.parse_unit(n, start, off));
|
||||
n = match try!(self.parse_first_char()) {
|
||||
Some(n) => n,
|
||||
None => return Ok(
|
||||
Duration::new(self.current.0, self.current.1 as u32)),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// Parse duration object
|
||||
///
|
||||
/// The duration object is a concatenation of time spans. Where each time
|
||||
/// span is an integer number and a suffix. Supported suffixes:
|
||||
/// * `nsec`, `ns` -- microseconds
|
||||
/// * `usec`, `us` -- microseconds
|
||||
/// * `msec`, `ms` -- milliseconds
|
||||
/// * `seconds`, `second`, `sec`, `s`
|
||||
/// * `minutes`, `minute`, `min`, `m`
|
||||
/// * `hours`, `hour`, `hr`, `h`
|
||||
/// * `days`, `day`, `d`
|
||||
/// * `weeks`, `week`, `w`
|
||||
/// * `months`, `month`, `M` -- defined as 30.44 days
|
||||
/// * `years`, `year`, `y` -- defined as 365.25 days
|
||||
///
|
||||
/// Examples:
|
||||
///
|
||||
/// ```
|
||||
/// use std::time::Duration;
|
||||
/// use humantime::parse_duration;
|
||||
///
|
||||
/// assert_eq!(parse_duration("2h 37min"), Ok(Duration::new(9420, 0)));
|
||||
/// ```
|
||||
pub fn parse_duration(s: &str) -> Result<Duration, Error> {
|
||||
Parser {
|
||||
iter: s.chars(),
|
||||
src: s,
|
||||
current: (0, 0),
|
||||
}.parse()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_units() {
|
||||
assert_eq!(parse_duration("17nsec"), Ok(Duration::new(0, 17)));
|
||||
assert_eq!(parse_duration("33ns"), Ok(Duration::new(0, 33)));
|
||||
assert_eq!(parse_duration("3usec"), Ok(Duration::new(0, 3000)));
|
||||
assert_eq!(parse_duration("78us"), Ok(Duration::new(0, 78000)));
|
||||
assert_eq!(parse_duration("31msec"), Ok(Duration::new(0, 31000000)));
|
||||
assert_eq!(parse_duration("6ms"), Ok(Duration::new(0, 6000000)));
|
||||
assert_eq!(parse_duration("3000s"), Ok(Duration::new(3000, 0)));
|
||||
assert_eq!(parse_duration("300sec"), Ok(Duration::new(300, 0)));
|
||||
assert_eq!(parse_duration("50seconds"), Ok(Duration::new(50, 0)));
|
||||
assert_eq!(parse_duration("1second"), Ok(Duration::new(1, 0)));
|
||||
assert_eq!(parse_duration("100m"), Ok(Duration::new(6000, 0)));
|
||||
assert_eq!(parse_duration("12min"), Ok(Duration::new(720, 0)));
|
||||
assert_eq!(parse_duration("1minute"), Ok(Duration::new(60, 0)));
|
||||
assert_eq!(parse_duration("7minutes"), Ok(Duration::new(420, 0)));
|
||||
assert_eq!(parse_duration("2h"), Ok(Duration::new(7200, 0)));
|
||||
assert_eq!(parse_duration("7hr"), Ok(Duration::new(25200, 0)));
|
||||
assert_eq!(parse_duration("1hour"), Ok(Duration::new(3600, 0)));
|
||||
assert_eq!(parse_duration("24hours"), Ok(Duration::new(86400, 0)));
|
||||
assert_eq!(parse_duration("1day"), Ok(Duration::new(86400, 0)));
|
||||
assert_eq!(parse_duration("2days"), Ok(Duration::new(172800, 0)));
|
||||
assert_eq!(parse_duration("365d"), Ok(Duration::new(31536000, 0)));
|
||||
assert_eq!(parse_duration("1week"), Ok(Duration::new(604800, 0)));
|
||||
assert_eq!(parse_duration("7weeks"), Ok(Duration::new(4233600, 0)));
|
||||
assert_eq!(parse_duration("52w"), Ok(Duration::new(31449600, 0)));
|
||||
assert_eq!(parse_duration("1month"), Ok(Duration::new(2630016, 0)));
|
||||
assert_eq!(parse_duration("3months"), Ok(Duration::new(3*2630016, 0)));
|
||||
assert_eq!(parse_duration("12M"), Ok(Duration::new(31560192, 0)));
|
||||
assert_eq!(parse_duration("1year"), Ok(Duration::new(31557600, 0)));
|
||||
assert_eq!(parse_duration("7years"), Ok(Duration::new(7*31557600, 0)));
|
||||
assert_eq!(parse_duration("17y"), Ok(Duration::new(536479200, 0)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_combo() {
|
||||
assert_eq!(parse_duration("20 min 17 nsec "), Ok(Duration::new(1200, 17)));
|
||||
assert_eq!(parse_duration("2h 15m"), Ok(Duration::new(8100, 0)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_overlow() {
|
||||
// Overflow on subseconds is earlier because of how we do conversion
|
||||
// we could fix it, but I don't see any good reason for this
|
||||
assert_eq!(parse_duration("100000000000000000000ns"),
|
||||
Err(Error::NumberOverflow));
|
||||
assert_eq!(parse_duration("100000000000000000us"),
|
||||
Err(Error::NumberOverflow));
|
||||
assert_eq!(parse_duration("100000000000000ms"),
|
||||
Err(Error::NumberOverflow));
|
||||
|
||||
assert_eq!(parse_duration("100000000000000000000s"),
|
||||
Err(Error::NumberOverflow));
|
||||
assert_eq!(parse_duration("10000000000000000000m"),
|
||||
Err(Error::NumberOverflow));
|
||||
assert_eq!(parse_duration("1000000000000000000h"),
|
||||
Err(Error::NumberOverflow));
|
||||
assert_eq!(parse_duration("100000000000000000d"),
|
||||
Err(Error::NumberOverflow));
|
||||
assert_eq!(parse_duration("10000000000000000w"),
|
||||
Err(Error::NumberOverflow));
|
||||
assert_eq!(parse_duration("1000000000000000M"),
|
||||
Err(Error::NumberOverflow));
|
||||
assert_eq!(parse_duration("10000000000000y"),
|
||||
Err(Error::NumberOverflow));
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
//! Human-friendly time parser
|
||||
//!
|
||||
//! Currently this only currently implements parsing of duration. Relative and
|
||||
//! absolute times may be added in future.
|
||||
|
||||
#[macro_use] extern crate quick_error;
|
||||
|
||||
mod duration;
|
||||
|
||||
pub use duration::{parse_duration, Error as DurationError};
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
commands:
|
||||
|
||||
cargo: !Command
|
||||
description: Run any cargo command
|
||||
container: ubuntu
|
||||
run: [cargo]
|
||||
|
||||
make: !Command
|
||||
description: Build the library
|
||||
container: ubuntu
|
||||
run: [cargo, build]
|
||||
|
||||
test: !Command
|
||||
description: Test the library
|
||||
container: ubuntu
|
||||
environ: { RUST_BACKTRACE: 1 }
|
||||
run: [cargo, test]
|
||||
|
||||
containers:
|
||||
|
||||
ubuntu:
|
||||
setup:
|
||||
- !Ubuntu trusty
|
||||
- !UbuntuUniverse ~
|
||||
- !Install [make, checkinstall, wget, ca-certificates,
|
||||
libssl-dev, build-essential]
|
||||
|
||||
- !TarInstall
|
||||
url: "http://static.rust-lang.org/dist/rust-1.8.0-x86_64-unknown-linux-gnu.tar.gz"
|
||||
script: "./install.sh --prefix=/usr \
|
||||
--components=rustc,rust-std-x86_64-unknown-linux-gnu,cargo"
|
||||
|
||||
environ:
|
||||
HOME: /work/target
|
||||
USER: pc
|
||||
Reference in New Issue
Block a user