use std::collections::BTreeMap;
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use crate::error::Result;
use crate::policy::ExpiryPolicy;
use super::{KeepsakeId, RelationDefinition, RelationId, SubjectRef};
mod record;
pub use record::KeepsakeRecord;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum LifecycleState {
Applied,
Revoked,
Expired,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum ExpiryCause {
Timed,
Fulfilled,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(tag = "state", rename_all = "snake_case")]
pub enum KeepsakeLifecycle {
Applied,
Revoked {
revoked_at: DateTime<Utc>,
},
Expired {
expired_at: DateTime<Utc>,
cause: ExpiryCause,
},
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Keepsake {
id: KeepsakeId,
subject: SubjectRef,
relation_id: RelationId,
expiry: ExpiryPolicy,
applied_at: DateTime<Utc>,
lifecycle: KeepsakeLifecycle,
metadata: BTreeMap<String, String>,
}
impl Keepsake {
pub fn applied(
id: KeepsakeId,
subject: SubjectRef,
relation: &RelationDefinition,
applied_at: DateTime<Utc>,
metadata: BTreeMap<String, String>,
) -> Result<Self> {
subject.validate()?;
relation.expiry.validate()?;
Ok(Self {
id,
subject,
relation_id: relation.id,
expiry: relation.expiry.clone(),
applied_at,
lifecycle: KeepsakeLifecycle::Applied,
metadata,
})
}
#[must_use]
pub const fn id(&self) -> KeepsakeId {
self.id
}
#[must_use]
pub const fn subject(&self) -> &SubjectRef {
&self.subject
}
#[must_use]
pub const fn relation_id(&self) -> RelationId {
self.relation_id
}
#[must_use]
pub const fn expiry(&self) -> &ExpiryPolicy {
&self.expiry
}
#[must_use]
pub const fn applied_at(&self) -> DateTime<Utc> {
self.applied_at
}
#[must_use]
pub const fn metadata(&self) -> &BTreeMap<String, String> {
&self.metadata
}
#[must_use]
pub const fn state(&self) -> LifecycleState {
match self.lifecycle {
KeepsakeLifecycle::Applied => LifecycleState::Applied,
KeepsakeLifecycle::Revoked { .. } => LifecycleState::Revoked,
KeepsakeLifecycle::Expired { .. } => LifecycleState::Expired,
}
}
#[must_use]
pub const fn lifecycle(&self) -> &KeepsakeLifecycle {
&self.lifecycle
}
#[must_use]
pub const fn is_active(&self) -> bool {
matches!(self.lifecycle, KeepsakeLifecycle::Applied)
}
#[must_use]
pub const fn is_revoked(&self) -> bool {
matches!(self.lifecycle, KeepsakeLifecycle::Revoked { .. })
}
#[must_use]
pub const fn is_expired(&self) -> bool {
matches!(self.lifecycle, KeepsakeLifecycle::Expired { .. })
}
#[must_use]
pub const fn expires_at(&self) -> Option<DateTime<Utc>> {
self.expiry.timed_expiry()
}
#[must_use]
pub const fn ended_at(&self) -> Option<DateTime<Utc>> {
match self.lifecycle {
KeepsakeLifecycle::Applied => None,
KeepsakeLifecycle::Revoked { revoked_at } => Some(revoked_at),
KeepsakeLifecycle::Expired { expired_at, .. } => Some(expired_at),
}
}
#[must_use]
pub const fn revoked_at(&self) -> Option<DateTime<Utc>> {
match self.lifecycle {
KeepsakeLifecycle::Revoked { revoked_at } => Some(revoked_at),
KeepsakeLifecycle::Applied | KeepsakeLifecycle::Expired { .. } => None,
}
}
#[must_use]
pub const fn expired_at(&self) -> Option<DateTime<Utc>> {
match self.lifecycle {
KeepsakeLifecycle::Expired { expired_at, .. } => Some(expired_at),
KeepsakeLifecycle::Applied | KeepsakeLifecycle::Revoked { .. } => None,
}
}
#[must_use]
pub const fn fulfilled_at(&self) -> Option<DateTime<Utc>> {
match self.lifecycle {
KeepsakeLifecycle::Expired {
expired_at,
cause: ExpiryCause::Fulfilled,
} => Some(expired_at),
KeepsakeLifecycle::Applied
| KeepsakeLifecycle::Revoked { .. }
| KeepsakeLifecycle::Expired {
cause: ExpiryCause::Timed,
..
} => None,
}
}
}