Skip to content

Commit

Permalink
Merge branch 'main' into filter-refs-by-spec
Browse files Browse the repository at this point in the history
  • Loading branch information
Byron committed Sep 3, 2022
2 parents 82ee79e + 8c46088 commit 1f6e5ab
Show file tree
Hide file tree
Showing 40 changed files with 796 additions and 413 deletions.
14 changes: 8 additions & 6 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion etc/check-package-size.sh
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ echo "in root: gitoxide CLI"
(enter git-traverse && indent cargo diet -n --package-size-limit 10KB)
(enter git-url && indent cargo diet -n --package-size-limit 20KB)
(enter git-validate && indent cargo diet -n --package-size-limit 5KB)
(enter git-date && indent cargo diet -n --package-size-limit 10KB)
(enter git-date && indent cargo diet -n --package-size-limit 15KB)
(enter git-filter && indent cargo diet -n --package-size-limit 5KB)
(enter git-lfs && indent cargo diet -n --package-size-limit 5KB)
(enter git-note && indent cargo diet -n --package-size-limit 5KB)
Expand Down
2 changes: 1 addition & 1 deletion git-commitgraph/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ serde1 = ["serde", "git-hash/serde1", "bstr/serde1"]

[dependencies]
git-features = { version = "^0.22.3", path = "../git-features", features = ["rustsha1"] }
git-hash = { version = "^0.9.8", path = "../git-hash" }
git-hash = { version = "^0.9.9", path = "../git-hash" }
git-chunk = { version = "^0.3.1", path = "../git-chunk" }

bstr = { version = "0.2.13", default-features = false, features = ["std"] }
Expand Down
5 changes: 4 additions & 1 deletion git-date/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ license = "MIT/Apache-2.0"
description = "A WIP crate of the gitoxide project parsing dates the way git does"
authors = ["Sebastian Thiel <sebastian.thiel@icloud.com>"]
edition = "2018"
include = ["src/**/*", "CHANGELOG.md"]

[lib]
doctest = false
Expand All @@ -18,12 +19,14 @@ serde1 = ["serde", "bstr/serde1"]
bstr = { version = "0.2.13", default-features = false, features = ["std"]}
serde = { version = "1.0.114", optional = true, default-features = false, features = ["derive"]}
itoa = "1.0.1"
time = { version = "0.3.2", default-features = false, features = ["local-offset", "formatting", "macros"] }
time = { version = "0.3.2", default-features = false, features = ["local-offset", "formatting", "macros", "parsing"] }
thiserror = "1.0.32"

document-features = { version = "0.2.0", optional = true }

[dev-dependencies]
git-testtools = { path = "../tests/tools"}
once_cell = "1.12.0"

[package.metadata.docs.rs]
all-features = true
Expand Down
5 changes: 3 additions & 2 deletions git-date/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,9 @@
///
pub mod time;

mod parse;
pub use parse::parse;
///
pub mod parse;
pub use parse::function::parse;

/// A timestamp with timezone.
#[derive(PartialEq, Eq, Debug, Hash, Ord, PartialOrd, Clone, Copy)]
Expand Down
139 changes: 131 additions & 8 deletions git-date/src/parse.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,134 @@
use crate::Time;

#[derive(thiserror::Error, Debug)]
#[allow(missing_docs)]
pub fn parse(input: &str) -> Option<Time> {
// TODO: actual implementation, this is just to not constantly fail
if input == "1979-02-26 18:30:00" {
Some(Time::new(42, 1800))
} else {
None
pub enum Error {
#[error("Cannot represent times before UNIX epoch at timestamp {timestamp}")]
TooEarly { timestamp: i64 },
#[error("Date string can not be parsed")]
InvalidDateString,
#[error("Dates past 2038 can not be represented.")]
InvalidDate(#[from] std::num::TryFromIntError),
#[error("Current time is missing.")]
MissingCurrentTime,
}

pub(crate) mod function {
use crate::parse::{relative, Error};
use crate::time::format::{DEFAULT, ISO8601, ISO8601_STRICT, RFC2822, SHORT};
use crate::time::Sign;
use crate::Time;
use std::convert::TryInto;
use std::str::FromStr;
use std::time::SystemTime;
use time::{Date, OffsetDateTime};

#[allow(missing_docs)]
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" {
return Ok(Time::new(42, 1800));
}

Ok(if let Ok(val) = Date::parse(input, SHORT) {
let val = val.with_hms(0, 0, 0).expect("date is in range").assume_utc();
Time::new(val.unix_timestamp().try_into()?, val.offset().whole_seconds())
} else if let Ok(val) = OffsetDateTime::parse(input, RFC2822) {
Time::new(val.unix_timestamp().try_into()?, val.offset().whole_seconds())
} else if let Ok(val) = OffsetDateTime::parse(input, ISO8601) {
Time::new(val.unix_timestamp().try_into()?, val.offset().whole_seconds())
} else if let Ok(val) = OffsetDateTime::parse(input, ISO8601_STRICT) {
Time::new(val.unix_timestamp().try_into()?, val.offset().whole_seconds())
} else if let Ok(val) = OffsetDateTime::parse(input, DEFAULT) {
Time::new(val.unix_timestamp().try_into()?, val.offset().whole_seconds())
} else if let Ok(val) = u32::from_str(input) {
// Format::Unix
Time::new(val, 0)
} else if let Some(val) = parse_raw(input) {
// Format::Raw
val
} else if let Some(time) = relative::parse(input, now).transpose()? {
Time::new(timestamp(time)?, time.offset().whole_seconds())
} else {
return Err(Error::InvalidDateString);
})
}

fn timestamp(date: OffsetDateTime) -> Result<u32, Error> {
let timestamp = date.unix_timestamp();
if timestamp < 0 {
Err(Error::TooEarly { timestamp })
} else {
Ok(timestamp.try_into()?)
}
}

fn parse_raw(input: &str) -> Option<Time> {
let mut split = input.split_whitespace();
let seconds_since_unix_epoch: u32 = split.next()?.parse().ok()?;
let offset = split.next()?;
if offset.len() != 5 {
return None;
}
let sign = if &offset[..1] == "-" { Sign::Plus } else { Sign::Minus };
let hours: i32 = offset[1..3].parse().ok()?;
let minutes: i32 = offset[3..5].parse().ok()?;
let offset_in_seconds = hours * 3600 + minutes * 60;
let time = Time {
seconds_since_unix_epoch,
offset_in_seconds,
sign,
};
Some(time)
}
}

mod relative {
use crate::parse::Error;
use std::convert::TryInto;
use std::str::FromStr;
use std::time::SystemTime;
use time::{Duration, OffsetDateTime};

fn parse_inner(input: &str) -> Option<Duration> {
let mut split = input.split_whitespace();
let multiplier = i64::from_str(split.next()?).ok()?;
let period = split.next()?;
if split.next()? != "ago" {
return None;
}
duration(period, multiplier)
}

pub(crate) fn parse(input: &str, now: Option<SystemTime>) -> Option<Result<OffsetDateTime, Error>> {
parse_inner(input).map(|offset| {
let offset = std::time::Duration::from_secs(offset.whole_seconds().try_into().expect("positive value"));
now.ok_or(Error::MissingCurrentTime).map(|now| {
now.checked_sub(offset)
.expect("BUG: values can't be large enough to cause underflow")
.into()
})
})
}

fn duration(period: &str, multiplier: i64) -> Option<Duration> {
let period = period.strip_suffix('s').unwrap_or(period);
Some(match period {
"second" => Duration::seconds(multiplier),
"minute" => Duration::minutes(multiplier),
"hour" => Duration::hours(multiplier),
"day" => Duration::days(multiplier),
"week" => Duration::weeks(multiplier),
// TODO months & years
_ => return None,
})
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn two_weeks_ago() {
assert_eq!(parse_inner("2 weeks ago"), Some(Duration::weeks(2)));
}
}
}
46 changes: 46 additions & 0 deletions git-date/tests/fixtures/generate_git_date_baseline.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
#!/bin/bash
set -eu -o pipefail

git init;

function baseline() {
local test_date=$1 # first argument is the date to test

git -c section.key="$test_date" config --type=expiry-date section.key && status=0 || status=$?
{
echo "$test_date"
echo "$status"
if [ $status == 0 ]
then
git -c section.key="$test_date" config --type=expiry-date section.key
else
echo "-1"
fi
} >> baseline.git
}

# success

# date formats following to https://git-scm.com/docs/git-log#Documentation/git-log.txt---dateltformatgt

# short
# ODO
#baseline '2022-08-22'
# rfc2822
baseline 'Thu, 18 Aug 2022 12:45:06 +0800'
# iso8601
baseline '2022-08-17 22:04:58 +0200'
# iso8601_strict
baseline '2022-08-17T21:43:13+08:00'
# default
baseline 'Thu Sep 04 2022 10:45:06 -0400'
# unix
baseline '123456789'
# raw
baseline '1660874655 +0800'

# failing

# empty_input
baseline ""

Git LFS file not shown

0 comments on commit 1f6e5ab

Please sign in to comment.