Skip to content

Commit 9ad1a5f

Browse files
committed
parse is pure function.
1 parent cce7616 commit 9ad1a5f

File tree

4 files changed

+38
-26
lines changed

4 files changed

+38
-26
lines changed

git-date/src/parse.rs

Lines changed: 19 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ use crate::Time;
44
use std::convert::TryInto;
55
use std::num::TryFromIntError;
66
use std::str::FromStr;
7+
use std::time::SystemTime;
78
use time::{Date, OffsetDateTime};
89

910
#[derive(thiserror::Error, Debug)]
@@ -15,10 +16,12 @@ pub enum Error {
1516
InvalidPeriod,
1617
#[error("Dates past 2038 can not be represented.")]
1718
InvalidDate(#[from] TryFromIntError),
19+
#[error("Current time is missing.")]
20+
MissingCurrentTime,
1821
}
1922

2023
#[allow(missing_docs)]
21-
pub fn parse(input: &str) -> Result<Time, Error> {
24+
pub fn parse(input: &str, now: Option<SystemTime>) -> Result<Time, Error> {
2225
// TODO: actual implementation, this is just to not constantly fail
2326
if input == "1979-02-26 18:30:00" {
2427
Ok(Time::new(42, 1800))
@@ -55,8 +58,11 @@ pub fn parse(input: &str) -> Result<Time, Error> {
5558
} else if let Some(val) = parse_raw(input) {
5659
// Format::Raw
5760
Ok(val)
58-
} else if let Some(val) = relative::parse(input) {
59-
Ok(Time::new(val.unix_timestamp() as u32, val.offset().whole_seconds()))
61+
} else if let Some(val) = relative::parse(input, now.ok_or(Error::MissingCurrentTime)?) {
62+
Ok(Time::new(
63+
val.unix_timestamp().try_into()?,
64+
val.offset().whole_seconds(),
65+
))
6066
} else {
6167
Err(Error::InvalidDateString)
6268
};
@@ -85,26 +91,27 @@ fn parse_raw(input: &str) -> Option<Time> {
8591
mod relative {
8692
use crate::parse::Error;
8793
use std::str::FromStr;
94+
use std::time::SystemTime;
8895
use time::{Duration, OffsetDateTime};
8996

90-
pub(crate) fn parse(input: &str) -> Option<OffsetDateTime> {
97+
pub(crate) fn parse(input: &str, now: SystemTime) -> Option<OffsetDateTime> {
9198
let mut split = input.split_whitespace();
9299
let multiplier = i64::from_str(split.next()?).ok()?;
93-
let period = period_to_seconds(split.next()?).ok()?;
100+
let period = split.next()?;
94101
if split.next()? != "ago" {
95102
return None;
96103
}
97-
OffsetDateTime::now_utc().checked_sub(Duration::seconds(multiplier * period))
104+
OffsetDateTime::from(now).checked_sub(duration(period, multiplier).ok()?)
98105
}
99106

100-
fn period_to_seconds(period: &str) -> Result<i64, Error> {
107+
fn duration(period: &str, multiplier: i64) -> Result<Duration, Error> {
101108
let period = period.strip_suffix("s").unwrap_or(period);
102109
return match period {
103-
"second" => Ok(1),
104-
"minute" => Ok(60),
105-
"hour" => Ok(60 * 60),
106-
"day" => Ok(24 * 60 * 60),
107-
"week" => Ok(7 * 24 * 60 * 60),
110+
"second" => Ok(Duration::seconds(multiplier)),
111+
"minute" => Ok(Duration::minutes(multiplier)),
112+
"hour" => Ok(Duration::hours(multiplier)),
113+
"day" => Ok(Duration::days(multiplier)),
114+
"week" => Ok(Duration::weeks(multiplier)),
108115
// TODO months & years
109116
_ => Err(Error::InvalidPeriod),
110117
};

git-date/tests/time/parse.rs

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@ use git_date::Time;
44
use once_cell::sync::Lazy;
55
use std::collections::HashMap;
66
use std::str::FromStr;
7-
use time::OffsetDateTime;
7+
use std::time::SystemTime;
8+
use time::{Duration, OffsetDateTime};
89

910
type Result<T = ()> = std::result::Result<T, Box<dyn std::error::Error>>;
1011

@@ -28,7 +29,7 @@ static BASELINE: Lazy<HashMap<BString, (usize, BString)>> = Lazy::new(|| {
2829
#[test]
2930
fn baseline() {
3031
for (pattern, (exit_code, output)) in BASELINE.iter() {
31-
let res = git_date::parse(pattern.to_str().expect("valid pattern"));
32+
let res = git_date::parse(pattern.to_str().expect("valid pattern"), Some(SystemTime::now()));
3233
assert_eq!(
3334
res.is_ok(),
3435
*exit_code == 0,
@@ -45,7 +46,7 @@ fn baseline() {
4546
#[test]
4647
fn special_time_is_ok_for_now() {
4748
assert_eq!(
48-
git_date::parse("1979-02-26 18:30:00").unwrap(),
49+
git_date::parse("1979-02-26 18:30:00", Some(SystemTime::now())).unwrap(),
4950
Time {
5051
seconds_since_unix_epoch: 42,
5152
offset_in_seconds: 1800,
@@ -57,7 +58,7 @@ fn special_time_is_ok_for_now() {
5758
#[test]
5859
fn short() {
5960
assert_eq!(
60-
git_date::parse("1979-02-26").expect("parsed date"),
61+
git_date::parse("1979-02-26", Some(SystemTime::now())).expect("parsed date"),
6162
Time {
6263
seconds_since_unix_epoch: 288835200,
6364
offset_in_seconds: 0,
@@ -70,7 +71,7 @@ fn short() {
7071
#[test]
7172
fn rfc2822() {
7273
assert_eq!(
73-
git_date::parse("Thu, 18 Aug 2022 12:45:06 +0800").expect("parsed rfc2822 string"),
74+
git_date::parse("Thu, 18 Aug 2022 12:45:06 +0800", Some(SystemTime::now())).expect("parsed rfc2822 string"),
7475
Time {
7576
seconds_since_unix_epoch: 1660797906,
7677
offset_in_seconds: 28800,
@@ -82,14 +83,16 @@ fn rfc2822() {
8283

8384
#[test]
8485
fn relative() {
85-
let two_weeks_ago = git_date::parse("2 weeks ago").expect("valid time");
86+
let now = Some(SystemTime::now());
87+
let two_weeks_ago = git_date::parse("2 weeks ago", now).expect("valid time");
8688
assert_eq!(Sign::Plus, two_weeks_ago.sign);
8789
assert_eq!(0, two_weeks_ago.offset_in_seconds);
90+
let expected = OffsetDateTime::from(now.unwrap()).saturating_sub(Duration::weeks(2));
91+
// account for the loss of precision when creating `Time` with seconds
92+
let expected = expected.replace_nanosecond(0).unwrap();
8893
assert_eq!(
89-
OffsetDateTime::from_unix_timestamp(two_weeks_ago.seconds_since_unix_epoch as i64)
90-
.expect("valid datetime")
91-
.iso_week(),
92-
OffsetDateTime::now_utc().iso_week() - 2,
93-
"weeks numbers differ"
94+
OffsetDateTime::from_unix_timestamp(two_weeks_ago.seconds_since_unix_epoch as i64).expect("valid datetime"),
95+
expected,
96+
"relative times differ"
9497
);
9598
}

git-repository/src/repository/identity.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use std::borrow::Cow;
2+
use std::time::SystemTime;
23

34
use crate::{bstr::BString, permission};
45

@@ -125,13 +126,13 @@ impl Personas {
125126
committer_email = committer_email.or_else(|| env_var("GIT_COMMITTER_EMAIL"));
126127
committer_date = std::env::var("GIT_COMMITTER_DATE")
127128
.ok()
128-
.and_then(|date| git_date::parse(&date).ok());
129+
.and_then(|date| git_date::parse(&date, Some(SystemTime::now())).ok());
129130

130131
author_name = author_name.or_else(|| env_var("GIT_AUTHOR_NAME"));
131132
author_email = author_email.or_else(|| env_var("GIT_AUTHOR_EMAIL"));
132133
author_date = std::env::var("GIT_AUTHOR_DATE")
133134
.ok()
134-
.and_then(|date| git_date::parse(&date).ok());
135+
.and_then(|date| git_date::parse(&date, Some(SystemTime::now())).ok());
135136

136137
user_email = user_email.or_else(|| env_var("EMAIL")); // NOTE: we don't have permission for this specific one…
137138
}

git-revision/src/spec/parse/function.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use std::time::SystemTime;
12
use std::{convert::TryInto, str::FromStr};
23

34
use bstr::{BStr, BString, ByteSlice, ByteVec};
@@ -435,7 +436,7 @@ where
435436
let time = nav
436437
.to_str()
437438
.ok()
438-
.and_then(|v| git_date::parse(v).ok())
439+
.and_then(|v| git_date::parse(v, Some(SystemTime::now())).ok())
439440
.ok_or_else(|| Error::Time { input: nav.into() })?;
440441
delegate
441442
.reflog(delegate::ReflogLookup::Date(time))

0 commit comments

Comments
 (0)