use std::time::SystemTime;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub enum ActionType {
Warning,
Note,
TempSuspension,
IndefSuspension,
Takedown,
}
impl ActionType {
pub fn contributes_strikes(self) -> bool {
match self {
ActionType::Note | ActionType::Warning => false,
ActionType::TempSuspension | ActionType::IndefSuspension | ActionType::Takedown => true,
}
}
pub fn is_suspension(self) -> bool {
matches!(
self,
ActionType::TempSuspension | ActionType::IndefSuspension
)
}
pub fn as_db_str(self) -> &'static str {
match self {
ActionType::Warning => "warning",
ActionType::Note => "note",
ActionType::TempSuspension => "temp_suspension",
ActionType::IndefSuspension => "indef_suspension",
ActionType::Takedown => "takedown",
}
}
pub fn from_db_str(s: &str) -> Option<Self> {
match s {
"warning" => Some(ActionType::Warning),
"note" => Some(ActionType::Note),
"temp_suspension" => Some(ActionType::TempSuspension),
"indef_suspension" => Some(ActionType::IndefSuspension),
"takedown" => Some(ActionType::Takedown),
_ => None,
}
}
}
#[derive(Debug, Clone)]
pub struct ActionRecord {
pub strike_value_applied: u32,
pub effective_at: SystemTime,
pub revoked_at: Option<SystemTime>,
pub action_type: ActionType,
pub expires_at: Option<SystemTime>,
pub was_dampened: bool,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn contributes_strikes_matches_design() {
assert!(!ActionType::Warning.contributes_strikes());
assert!(!ActionType::Note.contributes_strikes());
assert!(ActionType::TempSuspension.contributes_strikes());
assert!(ActionType::IndefSuspension.contributes_strikes());
assert!(ActionType::Takedown.contributes_strikes());
}
#[test]
fn is_suspension_only_for_temp_and_indef() {
assert!(!ActionType::Warning.is_suspension());
assert!(!ActionType::Note.is_suspension());
assert!(ActionType::TempSuspension.is_suspension());
assert!(ActionType::IndefSuspension.is_suspension());
assert!(
!ActionType::Takedown.is_suspension(),
"Takedown carries strikes but is not a suspension — it does not trigger decay freeze"
);
}
#[test]
fn db_str_round_trip_for_every_variant() {
for v in [
ActionType::Warning,
ActionType::Note,
ActionType::TempSuspension,
ActionType::IndefSuspension,
ActionType::Takedown,
] {
assert_eq!(ActionType::from_db_str(v.as_db_str()), Some(v));
}
}
#[test]
fn db_str_unknown_value_yields_none() {
assert!(ActionType::from_db_str("WARNING").is_none()); assert!(ActionType::from_db_str("ban").is_none());
assert!(ActionType::from_db_str("").is_none());
}
}