Skip to content

Commit 87c22b4

Browse files
authored
feat(prost-types): Implement conversion Duration to/from chrono::TimeDelta (#1236)
1 parent a2e8f57 commit 87c22b4

File tree

4 files changed

+59
-3
lines changed

4 files changed

+59
-3
lines changed

.github/workflows/ci.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -185,7 +185,7 @@ jobs:
185185
uses: model-checking/[email protected]
186186
with:
187187
args: |
188-
-p prost-types
188+
-p prost-types --features chrono
189189
no-std:
190190
runs-on: ubuntu-latest
191191
steps:

prost-types/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ arbitrary = ["dep:arbitrary"]
2020
[dependencies]
2121
prost = { version = "0.13.5", path = "../prost", default-features = false, features = ["prost-derive"] }
2222
arbitrary = { version = "1.4", features = ["derive"], optional = true }
23+
chrono = { version = "0.4.34", default-features = false, optional = true }
2324

2425
[dev-dependencies]
2526
proptest = "1"

prost-types/src/duration.rs

Lines changed: 56 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -183,13 +183,42 @@ impl FromStr for Duration {
183183
}
184184
}
185185

186+
#[cfg(feature = "chrono")]
187+
mod chrono {
188+
use ::chrono::TimeDelta;
189+
190+
use super::*;
191+
192+
impl From<::chrono::TimeDelta> for Duration {
193+
fn from(value: ::chrono::TimeDelta) -> Self {
194+
let mut result = Self {
195+
seconds: value.num_seconds(),
196+
nanos: value.subsec_nanos(),
197+
};
198+
result.normalize();
199+
result
200+
}
201+
}
202+
203+
impl TryFrom<Duration> for ::chrono::TimeDelta {
204+
type Error = DurationError;
205+
206+
fn try_from(mut value: Duration) -> Result<TimeDelta, duration::DurationError> {
207+
value.normalize();
208+
let seconds = TimeDelta::try_seconds(value.seconds).ok_or(DurationError::OutOfRange)?;
209+
let nanos = TimeDelta::nanoseconds(value.nanos.into());
210+
seconds.checked_add(&nanos).ok_or(DurationError::OutOfRange)
211+
}
212+
}
213+
}
214+
186215
#[cfg(kani)]
187216
mod proofs {
188217
use super::*;
189218

190219
#[cfg(feature = "std")]
191220
#[kani::proof]
192-
fn check_duration_roundtrip() {
221+
fn check_duration_std_roundtrip() {
193222
let seconds = kani::any();
194223
let nanos = kani::any();
195224
kani::assume(nanos < 1_000_000_000);
@@ -218,7 +247,7 @@ mod proofs {
218247

219248
#[cfg(feature = "std")]
220249
#[kani::proof]
221-
fn check_duration_roundtrip_nanos() {
250+
fn check_duration_std_roundtrip_nanos() {
222251
let seconds = 0;
223252
let nanos = kani::any();
224253
let std_duration = std::time::Duration::new(seconds, nanos);
@@ -243,6 +272,31 @@ mod proofs {
243272
))
244273
}
245274
}
275+
276+
#[cfg(feature = "chrono")]
277+
#[kani::proof]
278+
fn check_duration_chrono_roundtrip() {
279+
let seconds = kani::any();
280+
let nanos = kani::any();
281+
let prost_duration = Duration { seconds, nanos };
282+
match ::chrono::TimeDelta::try_from(prost_duration) {
283+
Err(DurationError::OutOfRange) => {
284+
// Test case not valid: duration out of range
285+
return;
286+
}
287+
Err(err) => {
288+
panic!("Unexpected error: {err}")
289+
}
290+
Ok(chrono_duration) => {
291+
let mut normalized_prost_duration = prost_duration;
292+
normalized_prost_duration.normalize();
293+
assert_eq!(
294+
Duration::try_from(chrono_duration).unwrap(),
295+
normalized_prost_duration
296+
);
297+
}
298+
}
299+
}
246300
}
247301

248302
#[cfg(test)]

prost-types/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
//! ## Feature Flags
3737
//! - `std`: Enable integration with standard library. Disable this feature for `no_std` support. This feature is enabled by default.
3838
//! - `arbitrary`: Enable integration with crate `arbitrary`. All types on this crate will implement `trait Arbitrary`.
39+
//! - `chrono`: Enable integration with crate `chrono`. Time related types implement conversions to/from their `chrono` equivalent.
3940
//!
4041
//! [1]: https://developers.google.com/protocol-buffers/docs/reference/google.protobuf
4142

0 commit comments

Comments
 (0)