use crate::types::{EntityId, GroupId, MicrosystemId, RelationshipId};
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum EventPayload {
Empty,
Interaction {
topic: Option<InteractionTopic>,
duration_minutes: u32,
},
SocialExclusion {
group_id: Option<GroupId>,
explicit: bool,
},
SocialInclusion {
group_id: Option<GroupId>,
},
BurdenFeedback {
source_relationship: Option<RelationshipId>,
verbal: bool,
},
Betrayal {
confidence_violated: f64,
},
Support {
support_type: SupportType,
effectiveness: f64,
},
Conflict {
verbal: bool,
physical: bool,
resolved: bool,
},
Violence {
weapon: Option<WeaponType>,
injury_severity: f64,
},
Humiliation {
public: bool,
perpetrator: Option<EntityId>,
},
Empowerment {
domain: LifeDomain,
},
Achievement {
domain: LifeDomain,
magnitude: f64,
},
Failure {
domain: LifeDomain,
public: bool,
},
Loss {
loss_type: LossType,
},
PolicyChange {
policy_area: PolicyArea,
favorability: f64,
},
ContextTransition {
from: MicrosystemId,
to: MicrosystemId,
},
HistoricalEvent {
event_type: HistoricalEventType,
scope: HistoricalScope,
},
Realization {
realization_type: RealizationType,
},
TraumaticExposure {
trauma_type: TraumaType,
proximity: f64,
},
}
impl EventPayload {
#[must_use]
pub fn life_domain(&self) -> Option<LifeDomain> {
match self {
EventPayload::Achievement { domain, .. } => Some(*domain),
EventPayload::Failure { domain, .. } => Some(*domain),
EventPayload::Empowerment { domain } => Some(*domain),
_ => None,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum InteractionTopic {
Work,
Personal,
Casual,
DeepConversation,
Conflict,
Support,
}
impl InteractionTopic {
#[must_use]
pub const fn name(&self) -> &'static str {
match self {
InteractionTopic::Work => "Work",
InteractionTopic::Personal => "Personal",
InteractionTopic::Casual => "Casual",
InteractionTopic::DeepConversation => "Deep Conversation",
InteractionTopic::Conflict => "Conflict",
InteractionTopic::Support => "Support",
}
}
#[must_use]
pub const fn all() -> [InteractionTopic; 6] {
[
InteractionTopic::Work,
InteractionTopic::Personal,
InteractionTopic::Casual,
InteractionTopic::DeepConversation,
InteractionTopic::Conflict,
InteractionTopic::Support,
]
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum LifeDomain {
Work,
Academic,
Social,
Athletic,
Creative,
Financial,
Health,
Relationship,
}
impl LifeDomain {
#[must_use]
pub const fn name(&self) -> &'static str {
match self {
LifeDomain::Work => "Work",
LifeDomain::Academic => "Academic",
LifeDomain::Social => "Social",
LifeDomain::Athletic => "Athletic",
LifeDomain::Creative => "Creative",
LifeDomain::Financial => "Financial",
LifeDomain::Health => "Health",
LifeDomain::Relationship => "Relationship",
}
}
#[must_use]
pub const fn all() -> [LifeDomain; 8] {
[
LifeDomain::Work,
LifeDomain::Academic,
LifeDomain::Social,
LifeDomain::Athletic,
LifeDomain::Creative,
LifeDomain::Financial,
LifeDomain::Health,
LifeDomain::Relationship,
]
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum PolicyArea {
Economic,
Social,
Environmental,
Healthcare,
Education,
Housing,
}
impl PolicyArea {
#[must_use]
pub const fn name(&self) -> &'static str {
match self {
PolicyArea::Economic => "Economic",
PolicyArea::Social => "Social",
PolicyArea::Environmental => "Environmental",
PolicyArea::Healthcare => "Healthcare",
PolicyArea::Education => "Education",
PolicyArea::Housing => "Housing",
}
}
#[must_use]
pub const fn all() -> [PolicyArea; 6] {
[
PolicyArea::Economic,
PolicyArea::Social,
PolicyArea::Environmental,
PolicyArea::Healthcare,
PolicyArea::Education,
PolicyArea::Housing,
]
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum HistoricalEventType {
War,
Pandemic,
EconomicCrisis,
NaturalDisaster,
PoliticalChange,
TechnologicalShift,
}
impl HistoricalEventType {
#[must_use]
pub const fn name(&self) -> &'static str {
match self {
HistoricalEventType::War => "War",
HistoricalEventType::Pandemic => "Pandemic",
HistoricalEventType::EconomicCrisis => "Economic Crisis",
HistoricalEventType::NaturalDisaster => "Natural Disaster",
HistoricalEventType::PoliticalChange => "Political Change",
HistoricalEventType::TechnologicalShift => "Technological Shift",
}
}
#[must_use]
pub const fn all() -> [HistoricalEventType; 6] {
[
HistoricalEventType::War,
HistoricalEventType::Pandemic,
HistoricalEventType::EconomicCrisis,
HistoricalEventType::NaturalDisaster,
HistoricalEventType::PoliticalChange,
HistoricalEventType::TechnologicalShift,
]
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum RealizationType {
SelfInsight,
RelationshipInsight,
MoralInsight,
ExistentialInsight,
}
impl RealizationType {
#[must_use]
pub const fn name(&self) -> &'static str {
match self {
RealizationType::SelfInsight => "Self Insight",
RealizationType::RelationshipInsight => "Relationship Insight",
RealizationType::MoralInsight => "Moral Insight",
RealizationType::ExistentialInsight => "Existential Insight",
}
}
#[must_use]
pub const fn all() -> [RealizationType; 4] {
[
RealizationType::SelfInsight,
RealizationType::RelationshipInsight,
RealizationType::MoralInsight,
RealizationType::ExistentialInsight,
]
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum SupportType {
Emotional,
Instrumental,
Informational,
Companionship,
}
impl SupportType {
#[must_use]
pub const fn name(&self) -> &'static str {
match self {
SupportType::Emotional => "Emotional",
SupportType::Instrumental => "Instrumental",
SupportType::Informational => "Informational",
SupportType::Companionship => "Companionship",
}
}
#[must_use]
pub const fn all() -> [SupportType; 4] {
[
SupportType::Emotional,
SupportType::Instrumental,
SupportType::Informational,
SupportType::Companionship,
]
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum WeaponType {
None,
Blunt,
Sharp,
Firearm,
}
impl WeaponType {
#[must_use]
pub const fn name(&self) -> &'static str {
match self {
WeaponType::None => "None",
WeaponType::Blunt => "Blunt",
WeaponType::Sharp => "Sharp",
WeaponType::Firearm => "Firearm",
}
}
#[must_use]
pub const fn all() -> [WeaponType; 4] {
[
WeaponType::None,
WeaponType::Blunt,
WeaponType::Sharp,
WeaponType::Firearm,
]
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum LossType {
Person,
Resource,
Status,
Opportunity,
}
impl LossType {
#[must_use]
pub const fn name(&self) -> &'static str {
match self {
LossType::Person => "Person",
LossType::Resource => "Resource",
LossType::Status => "Status",
LossType::Opportunity => "Opportunity",
}
}
#[must_use]
pub const fn all() -> [LossType; 4] {
[
LossType::Person,
LossType::Resource,
LossType::Status,
LossType::Opportunity,
]
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum TraumaType {
Physical,
Emotional,
Witnessing,
}
impl TraumaType {
#[must_use]
pub const fn name(&self) -> &'static str {
match self {
TraumaType::Physical => "Physical",
TraumaType::Emotional => "Emotional",
TraumaType::Witnessing => "Witnessing",
}
}
#[must_use]
pub const fn all() -> [TraumaType; 3] {
[
TraumaType::Physical,
TraumaType::Emotional,
TraumaType::Witnessing,
]
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum HistoricalScope {
Local,
Regional,
National,
Global,
}
impl HistoricalScope {
#[must_use]
pub const fn name(&self) -> &'static str {
match self {
HistoricalScope::Local => "Local",
HistoricalScope::Regional => "Regional",
HistoricalScope::National => "National",
HistoricalScope::Global => "Global",
}
}
#[must_use]
pub const fn all() -> [HistoricalScope; 4] {
[
HistoricalScope::Local,
HistoricalScope::Regional,
HistoricalScope::National,
HistoricalScope::Global,
]
}
}
#[cfg(test)]
mod tests {
use super::*;
fn unpack_interaction(payload: EventPayload) -> (Option<InteractionTopic>, u32) {
match payload {
EventPayload::Interaction {
topic,
duration_minutes,
} => (topic, duration_minutes),
_ => panic!("Expected Interaction payload"),
}
}
fn unpack_social_exclusion(payload: EventPayload) -> (Option<GroupId>, bool) {
match payload {
EventPayload::SocialExclusion { group_id, explicit } => (group_id, explicit),
_ => panic!("Expected SocialExclusion payload"),
}
}
fn unpack_support(payload: EventPayload) -> (SupportType, f64) {
match payload {
EventPayload::Support {
support_type,
effectiveness,
} => (support_type, effectiveness),
_ => panic!("Expected Support payload"),
}
}
fn unpack_violence(payload: EventPayload) -> (Option<WeaponType>, f64) {
match payload {
EventPayload::Violence {
weapon,
injury_severity,
} => (weapon, injury_severity),
_ => panic!("Expected Violence payload"),
}
}
#[test]
fn event_payload_interaction_creation() {
let payload = EventPayload::Interaction {
topic: Some(InteractionTopic::DeepConversation),
duration_minutes: 45,
};
let (topic, duration_minutes) = unpack_interaction(payload);
assert_eq!(topic, Some(InteractionTopic::DeepConversation));
assert_eq!(duration_minutes, 45);
}
#[test]
#[should_panic(expected = "Expected Interaction payload")]
fn event_payload_interaction_wrong_variant_panics() {
let payload = EventPayload::Support {
support_type: SupportType::Emotional,
effectiveness: 0.4,
};
let _ = unpack_interaction(payload);
}
#[test]
fn event_payload_social_exclusion_creation() {
let group = GroupId::new("team_alpha").unwrap();
let payload = EventPayload::SocialExclusion {
group_id: Some(group.clone()),
explicit: true,
};
let (group_id, explicit) = unpack_social_exclusion(payload);
assert_eq!(group_id, Some(group));
assert!(explicit);
}
#[test]
#[should_panic(expected = "Expected SocialExclusion payload")]
fn event_payload_social_exclusion_wrong_variant_panics() {
let payload = EventPayload::Interaction {
topic: None,
duration_minutes: 5,
};
let _ = unpack_social_exclusion(payload);
}
#[test]
fn event_payload_support_creation() {
let payload = EventPayload::Support {
support_type: SupportType::Emotional,
effectiveness: 0.8,
};
let (support_type, effectiveness) = unpack_support(payload);
assert_eq!(support_type, SupportType::Emotional);
assert!((effectiveness - 0.8).abs() < f64::EPSILON);
}
#[test]
#[should_panic(expected = "Expected Support payload")]
fn event_payload_support_wrong_variant_panics() {
let payload = EventPayload::Violence {
weapon: None,
injury_severity: 0.1,
};
let _ = unpack_support(payload);
}
#[test]
fn event_payload_violence_creation() {
let payload = EventPayload::Violence {
weapon: Some(WeaponType::Blunt),
injury_severity: 0.3,
};
let (weapon, injury_severity) = unpack_violence(payload);
assert_eq!(weapon, Some(WeaponType::Blunt));
assert!((injury_severity - 0.3).abs() < f64::EPSILON);
}
#[test]
#[should_panic(expected = "Expected Violence payload")]
fn event_payload_violence_wrong_variant_panics() {
let payload = EventPayload::Support {
support_type: SupportType::Instrumental,
effectiveness: 0.6,
};
let _ = unpack_violence(payload);
}
#[test]
fn event_payload_all_variants_constructible() {
let _ = EventPayload::Interaction {
topic: None,
duration_minutes: 0,
};
let _ = EventPayload::SocialExclusion {
group_id: None,
explicit: false,
};
let _ = EventPayload::SocialInclusion { group_id: None };
let _ = EventPayload::BurdenFeedback {
source_relationship: None,
verbal: false,
};
let _ = EventPayload::Betrayal {
confidence_violated: 0.5,
};
let _ = EventPayload::Support {
support_type: SupportType::Emotional,
effectiveness: 0.5,
};
let _ = EventPayload::Conflict {
verbal: true,
physical: false,
resolved: false,
};
let _ = EventPayload::Violence {
weapon: None,
injury_severity: 0.0,
};
let _ = EventPayload::Humiliation {
public: false,
perpetrator: None,
};
let _ = EventPayload::Empowerment {
domain: LifeDomain::Work,
};
let _ = EventPayload::Achievement {
domain: LifeDomain::Academic,
magnitude: 0.5,
};
let _ = EventPayload::Failure {
domain: LifeDomain::Financial,
public: false,
};
let _ = EventPayload::Loss {
loss_type: LossType::Person,
};
let _ = EventPayload::PolicyChange {
policy_area: PolicyArea::Healthcare,
favorability: 0.5,
};
let from = MicrosystemId::new("home").unwrap();
let to = MicrosystemId::new("work").unwrap();
let _ = EventPayload::ContextTransition { from, to };
let _ = EventPayload::HistoricalEvent {
event_type: HistoricalEventType::Pandemic,
scope: HistoricalScope::Global,
};
let _ = EventPayload::Realization {
realization_type: RealizationType::SelfInsight,
};
let _ = EventPayload::TraumaticExposure {
trauma_type: TraumaType::Witnessing,
proximity: 0.5,
};
}
#[test]
fn interaction_topic_all() {
let all = InteractionTopic::all();
assert_eq!(all.len(), 6);
}
#[test]
fn life_domain_all() {
let all = LifeDomain::all();
assert_eq!(all.len(), 8);
}
#[test]
fn policy_area_all() {
let all = PolicyArea::all();
assert_eq!(all.len(), 6);
}
#[test]
fn historical_event_type_all() {
let all = HistoricalEventType::all();
assert_eq!(all.len(), 6);
}
#[test]
fn realization_type_all() {
let all = RealizationType::all();
assert_eq!(all.len(), 4);
}
#[test]
fn support_type_all() {
let all = SupportType::all();
assert_eq!(all.len(), 4);
}
#[test]
fn weapon_type_all() {
let all = WeaponType::all();
assert_eq!(all.len(), 4);
}
#[test]
fn loss_type_all() {
let all = LossType::all();
assert_eq!(all.len(), 4);
}
#[test]
fn trauma_type_all() {
let all = TraumaType::all();
assert_eq!(all.len(), 3);
}
#[test]
fn historical_scope_all() {
let all = HistoricalScope::all();
assert_eq!(all.len(), 4);
}
#[test]
fn all_supporting_enums_have_names() {
for t in InteractionTopic::all() {
assert!(!t.name().is_empty());
}
for d in LifeDomain::all() {
assert!(!d.name().is_empty());
}
for a in PolicyArea::all() {
assert!(!a.name().is_empty());
}
for e in HistoricalEventType::all() {
assert!(!e.name().is_empty());
}
for r in RealizationType::all() {
assert!(!r.name().is_empty());
}
for s in SupportType::all() {
assert!(!s.name().is_empty());
}
for w in WeaponType::all() {
assert!(!w.name().is_empty());
}
for l in LossType::all() {
assert!(!l.name().is_empty());
}
for t in TraumaType::all() {
assert!(!t.name().is_empty());
}
for s in HistoricalScope::all() {
assert!(!s.name().is_empty());
}
}
#[test]
fn supporting_enums_are_copy() {
let t1 = InteractionTopic::Work;
let t2 = t1;
assert_eq!(t1, t2);
let d1 = LifeDomain::Academic;
let d2 = d1;
assert_eq!(d1, d2);
}
#[test]
fn event_payload_debug_format() {
let payload = EventPayload::Support {
support_type: SupportType::Emotional,
effectiveness: 0.8,
};
let debug = format!("{:?}", payload);
assert!(debug.contains("Support"));
assert!(debug.contains("Emotional"));
}
#[test]
fn event_payload_clone() {
let payload = EventPayload::Achievement {
domain: LifeDomain::Work,
magnitude: 0.9,
};
let cloned = payload.clone();
assert_eq!(payload, cloned);
}
#[test]
fn event_payload_life_domain_extraction() {
let achievement = EventPayload::Achievement {
domain: LifeDomain::Health,
magnitude: 0.7,
};
assert_eq!(achievement.life_domain(), Some(LifeDomain::Health));
let failure = EventPayload::Failure {
domain: LifeDomain::Financial,
public: false,
};
assert_eq!(failure.life_domain(), Some(LifeDomain::Financial));
let empowerment = EventPayload::Empowerment {
domain: LifeDomain::Academic,
};
assert_eq!(empowerment.life_domain(), Some(LifeDomain::Academic));
let empty = EventPayload::Empty;
assert_eq!(empty.life_domain(), None);
}
#[test]
fn event_payload_empty_variant() {
let payload = EventPayload::Empty;
let debug = format!("{:?}", payload);
assert!(debug.contains("Empty"));
let cloned = payload.clone();
assert_eq!(payload, cloned);
}
}