Skip to content

Commit

Permalink
parse is pure function.
Browse files Browse the repository at this point in the history
  • Loading branch information
svetli-n committed Aug 29, 2022
1 parent cce7616 commit 9ad1a5f
Show file tree
Hide file tree
Showing 4 changed files with 38 additions and 26 deletions.
31 changes: 19 additions & 12 deletions git-date/src/parse.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use crate::Time;
use std::convert::TryInto;
use std::num::TryFromIntError;
use std::str::FromStr;
use std::time::SystemTime;
use time::{Date, OffsetDateTime};

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

#[allow(missing_docs)]
pub fn parse(input: &str) -> Result<Time, Error> {
pub fn parse(input: &str, now: Option<SystemTime>) -> Result<Time, Error> {
// TODO: actual implementation, this is just to not constantly fail
if input == "1979-02-26 18:30:00" {
Ok(Time::new(42, 1800))
Expand Down Expand Up @@ -55,8 +58,11 @@ pub fn parse(input: &str) -> Result<Time, Error> {
} else if let Some(val) = parse_raw(input) {
// Format::Raw
Ok(val)
} else if let Some(val) = relative::parse(input) {
Ok(Time::new(val.unix_timestamp() as u32, val.offset().whole_seconds()))
} else if let Some(val) = relative::parse(input, now.ok_or(Error::MissingCurrentTime)?) {
Ok(Time::new(
val.unix_timestamp().try_into()?,
val.offset().whole_seconds(),
))
} else {
Err(Error::InvalidDateString)
};
Expand Down Expand Up @@ -85,26 +91,27 @@ fn parse_raw(input: &str) -> Option<Time> {
mod relative {
use crate::parse::Error;
use std::str::FromStr;
use std::time::SystemTime;
use time::{Duration, OffsetDateTime};

pub(crate) fn parse(input: &str) -> Option<OffsetDateTime> {
pub(crate) fn parse(input: &str, now: SystemTime) -> Option<OffsetDateTime> {
let mut split = input.split_whitespace();
let multiplier = i64::from_str(split.next()?).ok()?;
let period = period_to_seconds(split.next()?).ok()?;
let period = split.next()?;
if split.next()? != "ago" {
return None;
}
OffsetDateTime::now_utc().checked_sub(Duration::seconds(multiplier * period))
OffsetDateTime::from(now).checked_sub(duration(period, multiplier).ok()?)
}

fn period_to_seconds(period: &str) -> Result<i64, Error> {
fn duration(period: &str, multiplier: i64) -> Result<Duration, Error> {
let period = period.strip_suffix("s").unwrap_or(period);
return match period {
"second" => Ok(1),
"minute" => Ok(60),
"hour" => Ok(60 * 60),
"day" => Ok(24 * 60 * 60),
"week" => Ok(7 * 24 * 60 * 60),
"second" => Ok(Duration::seconds(multiplier)),
"minute" => Ok(Duration::minutes(multiplier)),
"hour" => Ok(Duration::hours(multiplier)),
"day" => Ok(Duration::days(multiplier)),
"week" => Ok(Duration::weeks(multiplier)),
// TODO months & years
_ => Err(Error::InvalidPeriod),
};
Expand Down
25 changes: 14 additions & 11 deletions git-date/tests/time/parse.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ use git_date::Time;
use once_cell::sync::Lazy;
use std::collections::HashMap;
use std::str::FromStr;
use time::OffsetDateTime;
use std::time::SystemTime;
use time::{Duration, OffsetDateTime};

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

Expand All @@ -28,7 +29,7 @@ static BASELINE: Lazy<HashMap<BString, (usize, BString)>> = Lazy::new(|| {
#[test]
fn baseline() {
for (pattern, (exit_code, output)) in BASELINE.iter() {
let res = git_date::parse(pattern.to_str().expect("valid pattern"));
let res = git_date::parse(pattern.to_str().expect("valid pattern"), Some(SystemTime::now()));
assert_eq!(
res.is_ok(),
*exit_code == 0,
Expand All @@ -45,7 +46,7 @@ fn baseline() {
#[test]
fn special_time_is_ok_for_now() {
assert_eq!(
git_date::parse("1979-02-26 18:30:00").unwrap(),
git_date::parse("1979-02-26 18:30:00", Some(SystemTime::now())).unwrap(),
Time {
seconds_since_unix_epoch: 42,
offset_in_seconds: 1800,
Expand All @@ -57,7 +58,7 @@ fn special_time_is_ok_for_now() {
#[test]
fn short() {
assert_eq!(
git_date::parse("1979-02-26").expect("parsed date"),
git_date::parse("1979-02-26", Some(SystemTime::now())).expect("parsed date"),
Time {
seconds_since_unix_epoch: 288835200,
offset_in_seconds: 0,
Expand All @@ -70,7 +71,7 @@ fn short() {
#[test]
fn rfc2822() {
assert_eq!(
git_date::parse("Thu, 18 Aug 2022 12:45:06 +0800").expect("parsed rfc2822 string"),
git_date::parse("Thu, 18 Aug 2022 12:45:06 +0800", Some(SystemTime::now())).expect("parsed rfc2822 string"),
Time {
seconds_since_unix_epoch: 1660797906,
offset_in_seconds: 28800,
Expand All @@ -82,14 +83,16 @@ fn rfc2822() {

#[test]
fn relative() {
let two_weeks_ago = git_date::parse("2 weeks ago").expect("valid time");
let now = Some(SystemTime::now());
let two_weeks_ago = git_date::parse("2 weeks ago", now).expect("valid time");
assert_eq!(Sign::Plus, two_weeks_ago.sign);
assert_eq!(0, two_weeks_ago.offset_in_seconds);
let expected = OffsetDateTime::from(now.unwrap()).saturating_sub(Duration::weeks(2));
// account for the loss of precision when creating `Time` with seconds
let expected = expected.replace_nanosecond(0).unwrap();
assert_eq!(
OffsetDateTime::from_unix_timestamp(two_weeks_ago.seconds_since_unix_epoch as i64)
.expect("valid datetime")
.iso_week(),
OffsetDateTime::now_utc().iso_week() - 2,
"weeks numbers differ"
OffsetDateTime::from_unix_timestamp(two_weeks_ago.seconds_since_unix_epoch as i64).expect("valid datetime"),
expected,
"relative times differ"
);
}
5 changes: 3 additions & 2 deletions git-repository/src/repository/identity.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use std::borrow::Cow;
use std::time::SystemTime;

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

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

author_name = author_name.or_else(|| env_var("GIT_AUTHOR_NAME"));
author_email = author_email.or_else(|| env_var("GIT_AUTHOR_EMAIL"));
author_date = std::env::var("GIT_AUTHOR_DATE")
.ok()
.and_then(|date| git_date::parse(&date).ok());
.and_then(|date| git_date::parse(&date, Some(SystemTime::now())).ok());

user_email = user_email.or_else(|| env_var("EMAIL")); // NOTE: we don't have permission for this specific one…
}
Expand Down
3 changes: 2 additions & 1 deletion git-revision/src/spec/parse/function.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use std::time::SystemTime;
use std::{convert::TryInto, str::FromStr};

use bstr::{BStr, BString, ByteSlice, ByteVec};
Expand Down Expand Up @@ -435,7 +436,7 @@ where
let time = nav
.to_str()
.ok()
.and_then(|v| git_date::parse(v).ok())
.and_then(|v| git_date::parse(v, Some(SystemTime::now())).ok())
.ok_or_else(|| Error::Time { input: nav.into() })?;
delegate
.reflog(delegate::ReflogLookup::Date(time))
Expand Down

0 comments on commit 9ad1a5f

Please sign in to comment.