|
3 | 3 | #[cfg(test)] |
4 | 4 | mod tests; |
5 | 5 |
|
6 | | -use chrono::{Duration, TimeZone, Utc}; |
| 6 | +use anyhow::Context; |
| 7 | +use chrono::{DateTime, Utc}; |
7 | 8 |
|
8 | 9 | use crate::{providers::CatalystSignedDocumentProvider, CatalystSignedDocument}; |
9 | 10 |
|
@@ -43,46 +44,49 @@ impl IdRule { |
43 | 44 | .ok_or(anyhow::anyhow!("Document `id` field must be a UUIDv7"))? |
44 | 45 | .to_unix(); |
45 | 46 |
|
46 | | - let id_time = Utc |
47 | | - .timestamp_opt(i64::try_from(id_time_secs).unwrap_or(0), id_time_nanos) |
48 | | - .single() |
49 | | - .ok_or_else(|| anyhow::anyhow!("Invalid timestamp in document `id` field"))?; |
| 47 | + let Some(id_time) = i64::try_from(id_time_secs) |
| 48 | + .ok() |
| 49 | + .and_then(|id_time_secs| DateTime::from_timestamp(id_time_secs, id_time_nanos)) |
| 50 | + else { |
| 51 | + doc.report().invalid_value( |
| 52 | + "id", |
| 53 | + &id.to_string(), |
| 54 | + "Must a valid UTC date time since `UNIX_EPOCH`", |
| 55 | + "Cannot instantiate a valid `DateTime<Utc>` value from the provided `id` field timestamp.", |
| 56 | + ); |
| 57 | + return Ok(false); |
| 58 | + }; |
50 | 59 |
|
51 | 60 | let now = Utc::now(); |
| 61 | + let time_delta = id_time.signed_duration_since(now); |
52 | 62 |
|
53 | | - let diff = id_time.signed_duration_since(now); |
54 | | - |
55 | | - if diff.num_nanoseconds().unwrap_or(0) > 0 { |
56 | | - // id_time is in the future |
| 63 | + if let Ok(id_age) = time_delta.to_std() { |
| 64 | + // `now` is earlier than `id_time` |
57 | 65 | if let Some(future_threshold) = provider.future_threshold() { |
58 | | - let threshold = Duration::from_std(future_threshold)?; |
59 | | - if diff > threshold { |
| 66 | + if id_age > future_threshold { |
60 | 67 | doc.report().invalid_value( |
61 | 68 | "id", |
62 | 69 | &id.to_string(), |
63 | 70 | "id < now + future_threshold", |
64 | | - &format!( |
65 | | - "Document Version timestamp {id} cannot be too far in future (threshold: {threshold:?}) from now: {now:?}" |
66 | | - ), |
| 71 | + &format!("Document ID timestamp {id} cannot be too far in future (threshold: {future_threshold:?}) from now: {now}"), |
67 | 72 | ); |
68 | 73 | is_valid = false; |
69 | 74 | } |
70 | 75 | } |
71 | 76 | } else { |
72 | | - // id_time is in the past |
73 | | - // make positive duration |
74 | | - let id_age = diff.abs(); |
| 77 | + // `id_time` is earlier than `now` |
| 78 | + let id_age = time_delta |
| 79 | + .abs() |
| 80 | + .to_std() |
| 81 | + .context("BUG! `id_time` must be earlier than `now` at this place")?; |
75 | 82 |
|
76 | 83 | if let Some(past_threshold) = provider.past_threshold() { |
77 | | - let threshold = Duration::from_std(past_threshold)?; |
78 | | - if id_age > threshold { |
| 84 | + if id_age > past_threshold { |
79 | 85 | doc.report().invalid_value( |
80 | 86 | "id", |
81 | 87 | &id.to_string(), |
82 | 88 | "id > now - past_threshold", |
83 | | - &format!( |
84 | | - "Document Version timestamp {id} cannot be too far behind (threshold: {threshold:?}) from now: {now:?}" |
85 | | - ), |
| 89 | + &format!("Document ID timestamp {id} cannot be too far behind (threshold: {past_threshold:?}) from now: {now:?}",), |
86 | 90 | ); |
87 | 91 | is_valid = false; |
88 | 92 | } |
|
0 commit comments