use serde::{Deserialize, Serialize};
use crate::error::CompositionError;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct TemporalFrame {
#[serde(rename = "issued-at")]
pub issued_at: u64,
#[serde(rename = "valid-from", default, skip_serializing_if = "Option::is_none")]
pub valid_from: Option<u64>,
#[serde(rename = "valid-until", default, skip_serializing_if = "Option::is_none")]
pub valid_until: Option<u64>,
#[serde(rename = "revoked-at", default, skip_serializing_if = "Option::is_none")]
pub revoked_at: Option<u64>,
#[serde(rename = "observed-at", default, skip_serializing_if = "Option::is_none")]
pub observed_at: Option<u64>,
}
impl TemporalFrame {
pub fn new(
issued_at: u64,
valid_from: Option<u64>,
valid_until: Option<u64>,
) -> Result<Self, CompositionError> {
let f = Self {
issued_at,
valid_from,
valid_until,
revoked_at: None,
observed_at: None,
};
f.validate_shape()?;
Ok(f)
}
pub fn validate_shape(&self) -> Result<(), CompositionError> {
if let (Some(vf), Some(vu)) = (self.valid_from, self.valid_until) {
if vf > vu {
return Err(CompositionError::Invariant(
"T-2: valid_from must be <= valid_until",
));
}
}
Ok(())
}
pub fn not_before(&self) -> u64 { self.valid_from.unwrap_or(self.issued_at) }
pub fn not_after(&self) -> Option<u64> { self.valid_until }
pub fn is_revoked(&self) -> bool { self.revoked_at.is_some() }
pub fn is_revoked_at(&self, now: u64) -> bool {
matches!(self.revoked_at, Some(t) if t <= now)
}
pub fn intersect(&self, other: &Self) -> Option<Self> {
let issued_at = self.issued_at.max(other.issued_at);
let valid_from = match (self.valid_from, other.valid_from) {
(Some(a), Some(b)) => Some(a.max(b)),
(Some(x), None) | (None, Some(x)) => Some(x),
(None, None) => None,
};
let valid_until = match (self.valid_until, other.valid_until) {
(Some(a), Some(b)) => Some(a.min(b)),
(Some(x), None) | (None, Some(x)) => Some(x),
(None, None) => None,
};
if let (Some(vf), Some(vu)) = (valid_from, valid_until) {
if vf > vu {
return None;
}
}
let revoked_at = match (self.revoked_at, other.revoked_at) {
(Some(a), Some(b)) => Some(a.min(b)),
(Some(x), None) | (None, Some(x)) => Some(x),
(None, None) => None,
};
Some(Self {
issued_at,
valid_from,
valid_until,
revoked_at,
observed_at: None,
})
}
}