use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use crate::error::{KeepsakeError, Result};
use crate::model::FulfillmentSnapshot;
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(tag = "type", rename_all = "snake_case")]
pub enum ExpiryPolicy {
ManualOnly,
At {
timestamp: DateTime<Utc>,
},
WhenFulfilled {
policy: FulfillmentPolicy,
},
}
impl ExpiryPolicy {
#[must_use]
pub const fn timed_expiry(&self) -> Option<DateTime<Utc>> {
match self {
Self::ManualOnly | Self::WhenFulfilled { .. } => None,
Self::At { timestamp } => Some(*timestamp),
}
}
pub const fn validate(&self) -> Result<()> {
match self {
Self::ManualOnly | Self::At { .. } => Ok(()),
Self::WhenFulfilled { policy } => policy.validate(),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(tag = "type", rename_all = "snake_case")]
pub enum FulfillmentPolicy {
CounterAtLeast {
key: String,
threshold: i64,
},
ChecklistComplete {
list_key: String,
},
}
impl FulfillmentPolicy {
pub const fn validate(&self) -> Result<()> {
match self {
Self::CounterAtLeast { threshold, .. } if *threshold <= 0 => {
Err(KeepsakeError::InvalidFulfillmentThreshold)
}
Self::CounterAtLeast { .. } | Self::ChecklistComplete { .. } => Ok(()),
}
}
#[must_use]
pub fn is_fulfilled(&self, snapshot: &FulfillmentSnapshot) -> bool {
match self {
Self::CounterAtLeast { key, threshold } => snapshot
.counters
.get(key)
.is_some_and(|value| value >= threshold),
Self::ChecklistComplete { list_key } => {
let mut matched = false;
for (key, complete) in &snapshot.checklist {
if key.starts_with(list_key) {
matched = true;
if !complete {
return false;
}
}
}
matched
}
}
}
}