use crate::enums::StatePath;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum SpiralType {
Stress,
Depression,
}
impl SpiralType {
#[must_use]
pub const fn name(&self) -> &'static str {
match self {
SpiralType::Stress => "Stress Spiral",
SpiralType::Depression => "Depression Spiral",
}
}
}
impl std::fmt::Display for SpiralType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.name())
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum AlertTrigger {
ThresholdExceeded(StatePath, f64),
SpiralDetected(SpiralType),
Custom(String),
}
impl AlertTrigger {
#[must_use]
pub const fn threshold(path: StatePath, value: f64) -> Self {
AlertTrigger::ThresholdExceeded(path, value)
}
#[must_use]
pub const fn spiral(spiral_type: SpiralType) -> Self {
AlertTrigger::SpiralDetected(spiral_type)
}
#[must_use]
pub fn custom(description: impl Into<String>) -> Self {
AlertTrigger::Custom(description.into())
}
#[must_use]
pub const fn is_threshold(&self) -> bool {
matches!(self, AlertTrigger::ThresholdExceeded(_, _))
}
#[must_use]
pub const fn is_spiral(&self) -> bool {
matches!(self, AlertTrigger::SpiralDetected(_))
}
#[must_use]
pub const fn is_custom(&self) -> bool {
matches!(self, AlertTrigger::Custom(_))
}
}
impl std::fmt::Display for AlertTrigger {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
AlertTrigger::ThresholdExceeded(path, value) => {
write!(f, "Threshold exceeded: {} = {:.2}", path, value)
}
AlertTrigger::SpiralDetected(spiral) => {
write!(f, "Spiral detected: {}", spiral)
}
AlertTrigger::Custom(desc) => {
write!(f, "Custom: {}", desc)
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::enums::MentalHealthPath;
fn matches_threshold(
trigger: &AlertTrigger,
expected_path: StatePath,
expected_value: f64,
) -> bool {
match trigger {
AlertTrigger::ThresholdExceeded(path, value) => {
let path_matches = *path == expected_path;
let value_matches = (*value - expected_value).abs() < f64::EPSILON;
path_matches && value_matches
}
_ => false,
}
}
fn matches_spiral(trigger: &AlertTrigger, expected: SpiralType) -> bool {
match trigger {
AlertTrigger::SpiralDetected(spiral_type) => *spiral_type == expected,
_ => false,
}
}
fn matches_custom(trigger: &AlertTrigger, expected: &str) -> bool {
match trigger {
AlertTrigger::Custom(desc) => desc == expected,
_ => false,
}
}
#[test]
fn alert_trigger_variants_exist() {
let threshold = AlertTrigger::ThresholdExceeded(
StatePath::MentalHealth(MentalHealthPath::SuicidalDesire),
0.7,
);
let spiral = AlertTrigger::SpiralDetected(SpiralType::Stress);
let custom = AlertTrigger::Custom("Test trigger".to_string());
assert!(threshold.is_threshold());
assert!(spiral.is_spiral());
assert!(custom.is_custom());
}
#[test]
fn threshold_trigger_constructor() {
let trigger =
AlertTrigger::threshold(StatePath::MentalHealth(MentalHealthPath::Depression), 0.8);
assert!(matches_threshold(
&trigger,
StatePath::MentalHealth(MentalHealthPath::Depression),
0.8
));
assert!(!matches_threshold(
&AlertTrigger::spiral(SpiralType::Stress),
StatePath::MentalHealth(MentalHealthPath::Depression),
0.8
));
}
#[test]
fn spiral_trigger_constructor() {
let trigger = AlertTrigger::spiral(SpiralType::Depression);
assert!(matches_spiral(&trigger, SpiralType::Depression));
assert!(!matches_spiral(
&AlertTrigger::threshold(StatePath::MentalHealth(MentalHealthPath::Depression), 0.5),
SpiralType::Depression
));
}
#[test]
fn custom_trigger_constructor() {
let trigger = AlertTrigger::custom("High risk detected");
assert!(matches_custom(&trigger, "High risk detected"));
assert!(!matches_custom(
&AlertTrigger::spiral(SpiralType::Stress),
"High risk detected"
));
}
#[test]
fn spiral_type_names() {
assert_eq!(SpiralType::Stress.name(), "Stress Spiral");
assert_eq!(SpiralType::Depression.name(), "Depression Spiral");
}
#[test]
fn spiral_type_display() {
assert_eq!(format!("{}", SpiralType::Stress), "Stress Spiral");
assert_eq!(format!("{}", SpiralType::Depression), "Depression Spiral");
}
#[test]
fn trigger_display() {
let threshold = AlertTrigger::ThresholdExceeded(
StatePath::MentalHealth(MentalHealthPath::SuicidalDesire),
0.75,
);
let display = format!("{}", threshold);
assert!(display.contains("Threshold exceeded"));
assert!(display.contains("0.75"));
let spiral = AlertTrigger::SpiralDetected(SpiralType::Stress);
let spiral_display = format!("{}", spiral);
assert!(spiral_display.contains("Spiral detected"));
assert!(spiral_display.contains("Stress"));
let custom = AlertTrigger::Custom("Test".to_string());
let custom_display = format!("{}", custom);
assert!(custom_display.contains("Custom"));
assert!(custom_display.contains("Test"));
}
#[test]
fn trigger_debug() {
let trigger = AlertTrigger::SpiralDetected(SpiralType::Depression);
let debug = format!("{:?}", trigger);
assert!(debug.contains("SpiralDetected"));
}
#[test]
fn trigger_clone() {
let original = AlertTrigger::Custom("Test".to_string());
let cloned = original.clone();
assert_eq!(original, cloned);
}
#[test]
fn spiral_type_clone_copy() {
let s1 = SpiralType::Stress;
let s2 = s1; let s3 = s1.clone();
assert_eq!(s1, s2);
assert_eq!(s1, s3);
}
#[test]
fn spiral_type_hash() {
use std::collections::HashSet;
let mut set = HashSet::new();
set.insert(SpiralType::Stress);
set.insert(SpiralType::Stress);
assert_eq!(set.len(), 1);
set.insert(SpiralType::Depression);
assert_eq!(set.len(), 2);
}
#[test]
fn is_methods_exclusive() {
let threshold =
AlertTrigger::threshold(StatePath::MentalHealth(MentalHealthPath::Depression), 0.5);
assert!(threshold.is_threshold());
assert!(!threshold.is_spiral());
assert!(!threshold.is_custom());
let spiral = AlertTrigger::spiral(SpiralType::Stress);
assert!(!spiral.is_threshold());
assert!(spiral.is_spiral());
assert!(!spiral.is_custom());
let custom = AlertTrigger::custom("test");
assert!(!custom.is_threshold());
assert!(!custom.is_spiral());
assert!(custom.is_custom());
}
#[test]
fn threshold_trigger_display_format() {
let trigger = AlertTrigger::ThresholdExceeded(
StatePath::MentalHealth(MentalHealthPath::SuicidalDesire),
0.75,
);
let display = format!("{}", trigger);
assert!(display.contains("Threshold exceeded"));
assert!(display.contains("0.75"));
assert!(display.contains("MentalHealth"));
}
#[test]
fn spiral_trigger_display_format() {
let trigger = AlertTrigger::SpiralDetected(SpiralType::Depression);
let display = format!("{}", trigger);
assert!(display.contains("Spiral detected"));
assert!(display.contains("Depression"));
}
#[test]
fn custom_trigger_display_format() {
let trigger = AlertTrigger::Custom("High risk intervention needed".to_string());
let display = format!("{}", trigger);
assert!(display.contains("Custom"));
assert!(display.contains("High risk intervention needed"));
}
}