Skip to content

Commit 759c85c

Browse files
committed
feat!: Use gix-error in gix-date
This will make for easier introspection for users of these errors.
1 parent 28219bb commit 759c85c

File tree

16 files changed

+100
-62
lines changed

16 files changed

+100
-62
lines changed

Cargo.lock

Lines changed: 2 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

gix-actor/src/signature/mod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ mod _ref {
1515
}
1616

1717
/// Try to parse the timestamp and create an owned instance from this shared one.
18-
pub fn to_owned(&self) -> Result<Signature, gix_date::parse::Error> {
18+
pub fn to_owned(&self) -> Result<Signature, gix_date::Error> {
1919
Ok(Signature {
2020
name: self.name.to_owned(),
2121
email: self.email.to_owned(),
@@ -58,7 +58,7 @@ mod _ref {
5858

5959
/// Parse the `time` field for access to the passed time since unix epoch, and the time offset.
6060
/// The format is expected to be [raw](gix_date::parse_header()).
61-
pub fn time(&self) -> Result<gix_date::Time, gix_date::parse::Error> {
61+
pub fn time(&self) -> Result<gix_date::Time, gix_date::Error> {
6262
self.time.parse()
6363
}
6464
}

gix-date/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,11 @@ doctest = false
1919
serde = ["dep:serde", "bstr/serde"]
2020

2121
[dependencies]
22+
gix-error = { version = "0.0.0", path = "../gix-error" }
2223
bstr = { version = "1.12.0", default-features = false, features = ["std"] }
2324
serde = { version = "1.0.114", optional = true, default-features = false, features = ["derive"] }
2425
itoa = "1.0.17"
2526
jiff = "0.2.17"
26-
thiserror = "2.0.17"
2727
# TODO: used for quick and easy `TimeBacking: std::io::Write` implementation, but could make that `Copy`
2828
# and remove this dep with custom impl
2929
smallvec = { version = "1.15.1", features = ["write"] }

gix-date/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ pub mod time;
1515
pub mod parse;
1616
pub use parse::function::{parse, parse_header};
1717

18+
pub use gix_error::ParseError as Error;
19+
1820
/// A timestamp with timezone.
1921
#[derive(Default, PartialEq, Eq, Debug, Hash, Ord, PartialOrd, Clone, Copy)]
2022
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]

gix-date/src/parse/function.rs

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,11 @@ use jiff::{civil::Date, fmt::rfc2822, tz::TimeZone, Zoned};
55
use crate::parse::git::parse_git_date_format;
66
use crate::parse::raw::parse_raw;
77
use crate::{
8-
parse::{relative, Error},
8+
parse::relative,
99
time::format::{DEFAULT, GITOXIDE, ISO8601, ISO8601_STRICT, SHORT},
10-
OffsetInSeconds, SecondsSinceUnixEpoch, Time,
10+
Error, OffsetInSeconds, SecondsSinceUnixEpoch, Time,
1111
};
12+
use gix_error::{Result, ResultExt};
1213

1314
/// Parse `input` as any time that Git can parse when inputting a date.
1415
///
@@ -75,7 +76,7 @@ pub fn parse(input: &str, now: Option<SystemTime>) -> Result<Time, Error> {
7576
Ok(if let Ok(val) = Date::strptime(SHORT.0, input) {
7677
let val = val
7778
.to_zoned(TimeZone::UTC)
78-
.map_err(|_| Error::InvalidDateString { input: input.into() })?;
79+
.or_raise(|| Error::new_with_input("Timezone conversion failed", input))?;
7980
Time::new(val.timestamp().as_second(), val.offset().seconds())
8081
} else if let Ok(val) = rfc2822_relaxed(input) {
8182
Time::new(val.timestamp().as_second(), val.offset().seconds())
@@ -97,7 +98,7 @@ pub fn parse(input: &str, now: Option<SystemTime>) -> Result<Time, Error> {
9798
// Format::Raw
9899
val
99100
} else {
100-
return Err(Error::InvalidDateString { input: input.into() });
101+
return Err(Error::new_with_input("Unknown date format", input))?;
101102
})
102103
}
103104

@@ -168,15 +169,15 @@ pub fn parse_header(input: &str) -> Option<Time> {
168169
/// whose weekdays are inconsistent with the date. While the day-of-week
169170
/// still must be parsed, it is otherwise ignored. This seems to be
170171
/// consistent with how `git` behaves.
171-
fn strptime_relaxed(fmt: &str, input: &str) -> Result<Zoned, jiff::Error> {
172+
fn strptime_relaxed(fmt: &str, input: &str) -> std::result::Result<Zoned, jiff::Error> {
172173
let mut tm = jiff::fmt::strtime::parse(fmt, input)?;
173174
tm.set_weekday(None);
174175
tm.to_zoned()
175176
}
176177

177178
/// This is just like strptime_relaxed, except for RFC 2822 parsing.
178179
/// Namely, it permits the weekday to be inconsistent with the date.
179-
fn rfc2822_relaxed(input: &str) -> Result<Zoned, jiff::Error> {
180+
fn rfc2822_relaxed(input: &str) -> std::result::Result<Zoned, jiff::Error> {
180181
static P: rfc2822::DateTimeParser = rfc2822::DateTimeParser::new().relaxed_weekday(true);
181182
P.parse_zoned(input)
182183
}

gix-date/src/parse/mod.rs

Lines changed: 2 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,8 @@
11
use std::str::FromStr;
22

3+
use crate::{Error, Time};
34
use smallvec::SmallVec;
45

5-
use crate::Time;
6-
7-
#[derive(thiserror::Error, Debug, Clone)]
8-
#[allow(missing_docs)]
9-
pub enum Error {
10-
#[error("Could not convert a duration into a date")]
11-
RelativeTimeConversion,
12-
#[error("Date string can not be parsed")]
13-
InvalidDateString { input: String },
14-
#[error("The heat-death of the universe happens before this date")]
15-
InvalidDate(#[from] std::num::TryFromIntError),
16-
#[error("Current time is missing but required to handle relative dates.")]
17-
MissingCurrentTime,
18-
}
19-
206
/// A container for just enough bytes to hold the largest-possible [`time`](Time) instance.
217
/// It's used in conjunction with
228
#[derive(Default, Clone)]
@@ -58,7 +44,7 @@ impl FromStr for Time {
5844
type Err = Error;
5945

6046
fn from_str(s: &str) -> Result<Self, Self::Err> {
61-
crate::parse_header(s).ok_or_else(|| Error::InvalidDateString { input: s.into() })
47+
crate::parse_header(s).ok_or_else(|| Error::new_with_input("invalid time", s))
6248
}
6349
}
6450

gix-date/src/parse/relative.rs

Lines changed: 16 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
use std::{str::FromStr, time::SystemTime};
22

3+
use crate::Error;
4+
use gix_error::{ensure, ParseError, Result, ResultExt};
35
use jiff::{tz::TimeZone, Span, Timestamp, Zoned};
46

5-
use crate::parse::Error;
6-
77
fn parse_inner(input: &str) -> Option<Result<Span, Error>> {
88
let mut split = input.split_whitespace();
99
let units = i64::from_str(split.next()?).ok()?;
@@ -15,26 +15,24 @@ fn parse_inner(input: &str) -> Option<Result<Span, Error>> {
1515
}
1616

1717
pub fn parse(input: &str, now: Option<SystemTime>) -> Option<Result<Zoned, Error>> {
18-
parse_inner(input).map(|result| {
18+
parse_inner(input).map(|result| -> Result<Zoned, Error> {
1919
let span = result?;
2020
// This was an error case in a previous version of this code, where
2121
// it would fail when converting from a negative signed integer
2222
// to an unsigned integer. This preserves that failure case even
2323
// though the code below handles it okay.
24-
if span.is_negative() {
25-
return Err(Error::RelativeTimeConversion);
26-
}
27-
now.ok_or(Error::MissingCurrentTime).and_then(|now| {
28-
let ts = Timestamp::try_from(now).map_err(|_| Error::RelativeTimeConversion)?;
29-
// N.B. This matches the behavior of this code when it was
30-
// written with `time`, but we might consider using the system
31-
// time zone here. If we did, then it would implement "1 day
32-
// ago" correctly, even when it crosses DST transitions. Since
33-
// we're in the UTC time zone here, which has no DST, 1 day is
34-
// in practice always 24 hours. ---AG
35-
let zdt = ts.to_zoned(TimeZone::UTC);
36-
zdt.checked_sub(span).map_err(|_| Error::RelativeTimeConversion)
37-
})
24+
ensure!(!span.is_negative(), ParseError::new(""));
25+
let now = now.ok_or(ParseError::new("Missing current time"))?;
26+
let ts: Timestamp = Timestamp::try_from(now).or_raise(|| Error::new("Could not convert current time"))?;
27+
// N.B. This matches the behavior of this code when it was
28+
// written with `time`, but we might consider using the system
29+
// time zone here. If we did, then it would implement "1 day
30+
// ago" correctly, even when it crosses DST transitions. Since
31+
// we're in the UTC time zone here, which has no DST, 1 day is
32+
// in practice always 24 hours. ---AG
33+
let zdt = ts.to_zoned(TimeZone::UTC);
34+
zdt.checked_sub(span)
35+
.or_raise(|| Error::new(format!("Failed to subtract {zdt} from {span}")))
3836
})
3937
}
4038

@@ -51,7 +49,7 @@ fn span(period: &str, units: i64) -> Option<Result<Span, Error>> {
5149
// Ignore values you don't know, assume seconds then (so does git)
5250
_anything => Span::new().try_seconds(units),
5351
};
54-
Some(result.map_err(|_| Error::RelativeTimeConversion))
52+
Some(result.or_raise(|| Error::new(format!("Couldn't parse span from '{period} {units}'"))))
5553
}
5654

5755
#[cfg(test)]

gix-date/tests/time/parse/mod.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ fn git_rfc2822() {
6363
}
6464

6565
#[test]
66-
fn raw() -> gix_testtools::Result {
66+
fn raw() -> gix_error::Result<(), gix_date::Error> {
6767
assert_eq!(
6868
gix_date::parse("1660874655 +0800", None)?,
6969
Time {
@@ -177,10 +177,10 @@ fn git_default() {
177177

178178
#[test]
179179
fn invalid_dates_can_be_produced_without_current_time() {
180-
assert!(matches!(
181-
gix_date::parse("foobar", None).unwrap_err(),
182-
gix_date::parse::Error::InvalidDateString { input } if input == "foobar"
183-
));
180+
assert_eq!(
181+
gix_date::parse("foobar", None).unwrap_err().to_string(),
182+
"Unknown date format: foobar"
183+
);
184184
}
185185

186186
/// Tests for compact ISO8601 formats (YYYYMMDDTHHMMSS variants)

gix-date/tests/time/parse/relative.rs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,12 @@ fn large_offsets() {
1010

1111
#[test]
1212
fn large_offsets_do_not_panic() {
13-
assert!(matches!(
14-
gix_date::parse("9999999999 weeks ago", Some(std::time::UNIX_EPOCH)),
15-
Err(gix_date::parse::Error::RelativeTimeConversion)
16-
));
13+
assert_eq!(
14+
gix_date::parse("9999999999 weeks ago", Some(std::time::UNIX_EPOCH))
15+
.unwrap_err()
16+
.to_string(),
17+
"Couldn't parse span from 'week 9999999999'"
18+
);
1719
}
1820

1921
#[test]

gix-error/Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,5 +15,6 @@ rust-version = "1.82"
1515
doctest = false
1616

1717
[dependencies]
18-
exn = "0.2.1"
18+
bstr = { version = "1.12.0", default-features = false }
19+
exn = { version = "0.2.1", default-features = false }
1920

0 commit comments

Comments
 (0)