1use canwu_core::{
4 ArmyId, BoundaryId, CommandId, EntityRef, EventId, KnowledgeHolderRef, LetterId, PersonId,
5 TerritoryId,
6};
7use canwu_time::SimTime;
8use serde::{Deserialize, Serialize};
9
10#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
11#[serde(tag = "type", content = "id", rename_all = "snake_case")]
12pub enum CauseRef {
13 Boundary(BoundaryId),
14 Command(CommandId),
15 Event(EventId),
16 System(String),
17}
18
19#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
20#[serde(tag = "type", rename_all = "snake_case")]
21pub enum EventKind {
22 MoveOrdered {
23 army: ArmyId,
24 from: TerritoryId,
25 to: TerritoryId,
26 arrival_at: SimTime,
27 },
28 ArmyArrived {
29 army: ArmyId,
30 territory: TerritoryId,
31 },
32 PersonMoveOrdered {
33 person: PersonId,
34 from: TerritoryId,
35 to: TerritoryId,
36 arrival_at: SimTime,
37 },
38 PersonArrived {
39 person: PersonId,
40 territory: TerritoryId,
41 },
42 LetterDelivered {
43 letter: LetterId,
44 carrier: PersonId,
45 recipient: PersonId,
46 territory: TerritoryId,
47 },
48 ReportDispatched {
49 recipient: PersonId,
50 army: ArmyId,
51 arrives_at: SimTime,
52 },
53 KnowledgeUpdated {
54 recipient: PersonId,
55 army: ArmyId,
56 known_location: TerritoryId,
57 },
58 KnowledgePublished {
59 holder: KnowledgeHolderRef,
60 record_count: u32,
61 },
62 DebugFieldChanged {
63 entity: EntityRef,
64 field: String,
65 old_value: String,
66 new_value: String,
67 },
68 Plugin {
69 plugin: String,
70 event_type: String,
71 },
72}
73
74#[derive(Clone, Debug, Default, Deserialize, Eq, Ord, PartialEq, PartialOrd, Serialize)]
81#[serde(tag = "type", content = "value", rename_all = "snake_case")]
82pub enum EventAudience {
83 Public,
84 Actor(PersonId),
85 Actors(Vec<PersonId>),
86 KnowledgeHolder(KnowledgeHolderRef),
87 AffectedActors,
90 #[default]
91 Private,
92}
93
94impl EventKind {
95 #[must_use]
96 pub const fn event_type(&self) -> &'static str {
97 match self {
98 Self::MoveOrdered { .. } => "move_ordered",
99 Self::ArmyArrived { .. } => "army_arrived",
100 Self::PersonMoveOrdered { .. } => "person_move_ordered",
101 Self::PersonArrived { .. } => "person_arrived",
102 Self::LetterDelivered { .. } => "letter_delivered",
103 Self::ReportDispatched { .. } => "report_dispatched",
104 Self::KnowledgeUpdated { .. } => "knowledge_updated",
105 Self::KnowledgePublished { .. } => "knowledge_published",
106 Self::DebugFieldChanged { .. } => "debug_field_changed",
107 Self::Plugin { .. } => "plugin",
108 }
109 }
110
111 #[must_use]
119 pub fn qualified_event_type(&self) -> String {
120 match self {
121 Self::Plugin { plugin, event_type } => format!("{plugin}.{event_type}"),
122 _ => self.event_type().to_owned(),
123 }
124 }
125}
126
127#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
128pub struct SimEvent {
129 pub id: EventId,
130 pub timestamp: SimTime,
131 pub kind: EventKind,
132 pub affected_entities: Vec<EntityRef>,
133 pub summary: String,
134 pub cause: Option<CauseRef>,
135 pub correlation_id: u64,
136}
137
138#[cfg(test)]
139mod tests {
140 use super::EventKind;
141 use canwu_core::{ArmyId, TerritoryId};
142
143 #[test]
144 fn qualified_event_type_preserves_legacy_labels_and_disambiguates_plugins() {
145 let built_in = EventKind::ArmyArrived {
146 army: ArmyId::new(1),
147 territory: TerritoryId::new(2),
148 };
149 assert_eq!(built_in.event_type(), "army_arrived");
150 assert_eq!(built_in.qualified_event_type(), "army_arrived");
151
152 let supply = EventKind::Plugin {
153 plugin: "example-supply".to_owned(),
154 event_type: "grain_allocated".to_owned(),
155 };
156 let demand = EventKind::Plugin {
157 plugin: "example-demand".to_owned(),
158 event_type: "grain_allocated".to_owned(),
159 };
160
161 assert_eq!(supply.event_type(), "plugin");
162 assert_eq!(
163 supply.qualified_event_type(),
164 "example-supply.grain_allocated"
165 );
166 assert_eq!(
167 demand.qualified_event_type(),
168 "example-demand.grain_allocated"
169 );
170 assert_ne!(supply.qualified_event_type(), demand.qualified_event_type());
171 }
172}