feat!: type Annotations.lastModified as a string (#956)
This commit is contained in:
parent
11f525c503
commit
a7194590ff
5 changed files with 70 additions and 14 deletions
|
|
@ -20,20 +20,23 @@ pub struct Annotations {
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
pub priority: Option<f32>,
|
pub priority: Option<f32>,
|
||||||
#[serde(skip_serializing_if = "Option::is_none", rename = "lastModified")]
|
#[serde(skip_serializing_if = "Option::is_none", rename = "lastModified")]
|
||||||
pub last_modified: Option<DateTime<Utc>>,
|
pub last_modified: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Annotations {
|
impl Annotations {
|
||||||
/// Creates a new Annotations instance specifically for resources
|
/// Creates annotations for a resource with priority and an RFC 3339 timestamp.
|
||||||
/// optional priority, and a timestamp (defaults to now if None)
|
///
|
||||||
|
/// # Panics
|
||||||
|
///
|
||||||
|
/// Panics if `priority` is not in the inclusive range `0.0..=1.0`.
|
||||||
pub fn for_resource(priority: f32, timestamp: DateTime<Utc>) -> Self {
|
pub fn for_resource(priority: f32, timestamp: DateTime<Utc>) -> Self {
|
||||||
assert!(
|
assert!(
|
||||||
(0.0..=1.0).contains(&priority),
|
(0.0..=1.0).contains(&priority),
|
||||||
"Priority {priority} must be between 0.0 and 1.0"
|
"Priority {priority} must be between 0.0 and 1.0"
|
||||||
);
|
);
|
||||||
Annotations {
|
Self {
|
||||||
priority: Some(priority),
|
priority: Some(priority),
|
||||||
last_modified: Some(timestamp),
|
last_modified: Some(timestamp.to_rfc3339()),
|
||||||
audience: None,
|
audience: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -48,12 +51,69 @@ impl Annotations {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Sets `lastModified` from a typed timestamp, serialized as RFC 3339.
|
||||||
pub fn with_timestamp(mut self, timestamp: DateTime<Utc>) -> Self {
|
pub fn with_timestamp(mut self, timestamp: DateTime<Utc>) -> Self {
|
||||||
self.last_modified = Some(timestamp);
|
self.last_modified = Some(timestamp.to_rfc3339());
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Sets `lastModified` to the current time, serialized as RFC 3339.
|
||||||
pub fn with_timestamp_now(self) -> Self {
|
pub fn with_timestamp_now(self) -> Self {
|
||||||
self.with_timestamp(Utc::now())
|
self.with_timestamp(Utc::now())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use chrono::TimeZone;
|
||||||
|
use serde_json::json;
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
fn annotations_of(value: serde_json::Value) -> Annotations {
|
||||||
|
serde_json::from_value::<Annotations>(value).unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn preserves_date_only_last_modified_verbatim() {
|
||||||
|
let annotations = annotations_of(json!({ "lastModified": "2025-01-12" }));
|
||||||
|
assert_eq!(annotations.last_modified.as_deref(), Some("2025-01-12"));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn preserves_rfc3339_last_modified_verbatim() {
|
||||||
|
let value = "2025-01-12T15:00:58Z";
|
||||||
|
let annotations = annotations_of(json!({ "lastModified": value }));
|
||||||
|
assert_eq!(annotations.last_modified.as_deref(), Some(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn missing_last_modified_is_none() {
|
||||||
|
let annotations = annotations_of(json!({}));
|
||||||
|
assert_eq!(annotations.last_modified, None);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn null_last_modified_is_none() {
|
||||||
|
let annotations = annotations_of(json!({ "lastModified": null }));
|
||||||
|
assert_eq!(annotations.last_modified, None);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn invalid_last_modified_string_is_preserved() {
|
||||||
|
let annotations = annotations_of(json!({ "lastModified": "not-a-date" }));
|
||||||
|
assert_eq!(annotations.last_modified.as_deref(), Some("not-a-date"));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn timestamp_round_trips_as_rfc3339_string() {
|
||||||
|
let timestamp = Utc.with_ymd_and_hms(2025, 1, 12, 15, 0, 58).unwrap();
|
||||||
|
let annotations = Annotations::default().with_timestamp(timestamp);
|
||||||
|
|
||||||
|
let value = serde_json::to_value(&annotations).unwrap();
|
||||||
|
assert_eq!(value["lastModified"], json!(timestamp.to_rfc3339()));
|
||||||
|
|
||||||
|
let round_tripped: Annotations = serde_json::from_value(value).unwrap();
|
||||||
|
assert_eq!(round_tripped, annotations);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -54,8 +54,7 @@
|
||||||
"type": [
|
"type": [
|
||||||
"string",
|
"string",
|
||||||
"null"
|
"null"
|
||||||
],
|
]
|
||||||
"format": "date-time"
|
|
||||||
},
|
},
|
||||||
"priority": {
|
"priority": {
|
||||||
"type": [
|
"type": [
|
||||||
|
|
|
||||||
|
|
@ -54,8 +54,7 @@
|
||||||
"type": [
|
"type": [
|
||||||
"string",
|
"string",
|
||||||
"null"
|
"null"
|
||||||
],
|
]
|
||||||
"format": "date-time"
|
|
||||||
},
|
},
|
||||||
"priority": {
|
"priority": {
|
||||||
"type": [
|
"type": [
|
||||||
|
|
|
||||||
|
|
@ -54,8 +54,7 @@
|
||||||
"type": [
|
"type": [
|
||||||
"string",
|
"string",
|
||||||
"null"
|
"null"
|
||||||
],
|
]
|
||||||
"format": "date-time"
|
|
||||||
},
|
},
|
||||||
"priority": {
|
"priority": {
|
||||||
"type": [
|
"type": [
|
||||||
|
|
|
||||||
|
|
@ -54,8 +54,7 @@
|
||||||
"type": [
|
"type": [
|
||||||
"string",
|
"string",
|
||||||
"null"
|
"null"
|
||||||
],
|
]
|
||||||
"format": "date-time"
|
|
||||||
},
|
},
|
||||||
"priority": {
|
"priority": {
|
||||||
"type": [
|
"type": [
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue