use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use super::passport::PassportId;
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct DerogationRef {
pub category: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub act_citation: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase", tag = "kind")]
#[non_exhaustive]
pub enum DeactivationReason {
Recycled,
Destroyed {
derogation: DerogationRef,
},
Exported,
Lost,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct EolEvent {
pub passport_id: PassportId,
pub reason: DeactivationReason,
pub declared_by: String,
pub declared_at: DateTime<Utc>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub material_recovery: Option<serde_json::Value>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub notes: Option<String>,
}
impl EolEvent {
#[must_use]
pub fn new(
passport_id: PassportId,
reason: DeactivationReason,
declared_by: impl Into<String>,
) -> Self {
Self {
passport_id,
reason,
declared_by: declared_by.into(),
declared_at: Utc::now(),
material_recovery: None,
notes: None,
}
}
#[must_use]
pub fn requires_derogation(&self) -> bool {
matches!(self.reason, DeactivationReason::Destroyed { .. })
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn recycled_roundtrips_and_needs_no_derogation() {
let e = EolEvent::new(PassportId::new(), DeactivationReason::Recycled, "did:web:r");
assert!(!e.requires_derogation());
let v = serde_json::to_value(&e).unwrap();
assert_eq!(v["reason"]["kind"], "recycled");
let back: EolEvent = serde_json::from_value(v).unwrap();
assert_eq!(back, e);
}
#[test]
fn destroyed_carries_a_derogation() {
let e = EolEvent::new(
PassportId::new(),
DeactivationReason::Destroyed {
derogation: DerogationRef {
category: "health-and-safety".into(),
act_citation: Some("Delegated Reg. (EU) 2026/xxx".into()),
},
},
"did:web:r",
);
assert!(e.requires_derogation());
let v = serde_json::to_value(&e).unwrap();
assert_eq!(v["reason"]["kind"], "destroyed");
assert_eq!(v["reason"]["derogation"]["category"], "health-and-safety");
}
}