Skip to main content

issun/plugin/holacracy/
events.rs

1//! Events for holacracy plugin
2//!
3//! Defines command events (requests) and state events (results) for task management,
4//! bidding, role assignment, and organizational dynamics.
5
6use crate::event::Event;
7use serde::{Deserialize, Serialize};
8
9use super::state::HolacracyMember;
10use super::types::*;
11
12// ============================================================================
13// Command Events (Requests)
14// ============================================================================
15
16/// Request to add a new task to the pool
17#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
18pub struct TaskAddRequested {
19    pub task: Task,
20}
21
22impl Event for TaskAddRequested {}
23
24/// Request to start bidding on a task
25#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
26pub struct BiddingStartRequested {
27    pub task_id: TaskId,
28}
29
30impl Event for BiddingStartRequested {}
31
32/// Request to submit a bid for a task
33#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
34pub struct BidSubmitRequested {
35    pub bid: Bid,
36}
37
38impl Event for BidSubmitRequested {}
39
40/// Request to assign a task to a member
41#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
42pub struct TaskAssignRequested {
43    pub task_id: TaskId,
44    pub member_id: MemberId,
45}
46
47impl Event for TaskAssignRequested {}
48
49/// Request to complete a task
50#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
51pub struct TaskCompleteRequested {
52    pub task_id: TaskId,
53}
54
55impl Event for TaskCompleteRequested {}
56
57/// Request to cancel a task
58#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
59pub struct TaskCancelRequested {
60    pub task_id: TaskId,
61    pub reason: String,
62}
63
64impl Event for TaskCancelRequested {}
65
66/// Request to add a new member
67#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
68pub struct MemberAddRequested {
69    pub member: HolacracyMember,
70}
71
72impl Event for MemberAddRequested {}
73
74/// Request to remove a member
75#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
76pub struct MemberRemoveRequested {
77    pub member_id: MemberId,
78}
79
80impl Event for MemberRemoveRequested {}
81
82/// Request to assign a role to a member
83#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
84pub struct RoleAssignRequested {
85    pub circle_id: CircleId,
86    pub role_id: RoleId,
87    pub member_id: MemberId,
88}
89
90impl Event for RoleAssignRequested {}
91
92/// Request to unassign a role from a member
93#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
94pub struct RoleUnassignRequested {
95    pub circle_id: CircleId,
96    pub role_id: RoleId,
97}
98
99impl Event for RoleUnassignRequested {}
100
101/// Request to create a new circle
102#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
103pub struct CircleCreateRequested {
104    pub circle: Circle,
105}
106
107impl Event for CircleCreateRequested {}
108
109/// Request to process bidding period expiration
110#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
111pub struct BiddingProcessRequested {
112    pub current_turn: u64,
113}
114
115impl Event for BiddingProcessRequested {}
116
117// ============================================================================
118// State Events (Results)
119// ============================================================================
120
121/// Task was added to the pool
122#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
123pub struct TaskAddedEvent {
124    pub task_id: TaskId,
125}
126
127impl Event for TaskAddedEvent {}
128
129/// Bidding started for a task
130#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
131pub struct BiddingStartedEvent {
132    pub task_id: TaskId,
133    pub started_at: u64,
134}
135
136impl Event for BiddingStartedEvent {}
137
138/// Bid was submitted for a task
139#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
140pub struct BidSubmittedEvent {
141    pub task_id: TaskId,
142    pub member_id: MemberId,
143    pub score: BidScore,
144}
145
146impl Event for BidSubmittedEvent {}
147
148/// Bid was rejected (member not eligible)
149#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
150pub struct BidRejectedEvent {
151    pub task_id: TaskId,
152    pub member_id: MemberId,
153    pub reason: String,
154}
155
156impl Event for BidRejectedEvent {}
157
158/// Task was assigned to a member
159#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
160pub struct TaskAssignedEvent {
161    pub task_id: TaskId,
162    pub member_id: MemberId,
163}
164
165impl Event for TaskAssignedEvent {}
166
167/// Task assignment failed
168#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
169pub struct TaskAssignmentFailedEvent {
170    pub task_id: TaskId,
171    pub member_id: MemberId,
172    pub reason: String,
173}
174
175impl Event for TaskAssignmentFailedEvent {}
176
177/// Task was completed
178#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
179pub struct TaskCompletedEvent {
180    pub task_id: TaskId,
181    pub completed_by: MemberId,
182    pub completed_at: u64,
183}
184
185impl Event for TaskCompletedEvent {}
186
187/// Task was cancelled
188#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
189pub struct TaskCancelledEvent {
190    pub task_id: TaskId,
191    pub reason: String,
192}
193
194impl Event for TaskCancelledEvent {}
195
196/// Member was added
197#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
198pub struct MemberAddedEvent {
199    pub member_id: MemberId,
200}
201
202impl Event for MemberAddedEvent {}
203
204/// Member was removed
205#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
206pub struct MemberRemovedEvent {
207    pub member_id: MemberId,
208}
209
210impl Event for MemberRemovedEvent {}
211
212/// Role was assigned to a member
213#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
214pub struct RoleAssignedEvent {
215    pub circle_id: CircleId,
216    pub role_id: RoleId,
217    pub member_id: MemberId,
218}
219
220impl Event for RoleAssignedEvent {}
221
222/// Role was unassigned from a member
223#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
224pub struct RoleUnassignedEvent {
225    pub circle_id: CircleId,
226    pub role_id: RoleId,
227    pub previous_holder: MemberId,
228}
229
230impl Event for RoleUnassignedEvent {}
231
232/// Role assignment failed
233#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
234pub struct RoleAssignmentFailedEvent {
235    pub circle_id: CircleId,
236    pub role_id: RoleId,
237    pub member_id: MemberId,
238    pub reason: String,
239}
240
241impl Event for RoleAssignmentFailedEvent {}
242
243/// Circle was created
244#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
245pub struct CircleCreatedEvent {
246    pub circle_id: CircleId,
247}
248
249impl Event for CircleCreatedEvent {}
250
251/// Bidding period expired and tasks were auto-assigned
252#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
253pub struct BiddingCompletedEvent {
254    pub task_id: TaskId,
255    pub assigned_to: Option<MemberId>,
256    pub bid_count: usize,
257}
258
259impl Event for BiddingCompletedEvent {}
260
261#[cfg(test)]
262mod tests {
263    use super::*;
264
265    #[test]
266    fn test_task_add_requested_creation() {
267        let task = Task::new("t1", "Test task");
268        let event = TaskAddRequested { task: task.clone() };
269        assert_eq!(event.task.id, "t1");
270    }
271
272    #[test]
273    fn test_bidding_start_requested_creation() {
274        let event = BiddingStartRequested {
275            task_id: "t1".to_string(),
276        };
277        assert_eq!(event.task_id, "t1");
278    }
279
280    #[test]
281    fn test_bid_submit_requested_creation() {
282        let score = BidScore::new(0.8, 0.6, 0.9);
283        let bid = Bid::new("t1", "alice", score, 50);
284        let event = BidSubmitRequested { bid: bid.clone() };
285        assert_eq!(event.bid.task_id, "t1");
286    }
287
288    #[test]
289    fn test_task_assign_requested_creation() {
290        let event = TaskAssignRequested {
291            task_id: "t1".to_string(),
292            member_id: "alice".to_string(),
293        };
294        assert_eq!(event.task_id, "t1");
295        assert_eq!(event.member_id, "alice");
296    }
297
298    #[test]
299    fn test_task_complete_requested_creation() {
300        let event = TaskCompleteRequested {
301            task_id: "t1".to_string(),
302        };
303        assert_eq!(event.task_id, "t1");
304    }
305
306    #[test]
307    fn test_task_cancel_requested_creation() {
308        let event = TaskCancelRequested {
309            task_id: "t1".to_string(),
310            reason: "No longer needed".to_string(),
311        };
312        assert_eq!(event.task_id, "t1");
313        assert_eq!(event.reason, "No longer needed");
314    }
315
316    #[test]
317    fn test_task_added_event_creation() {
318        let event = TaskAddedEvent {
319            task_id: "t1".to_string(),
320        };
321        assert_eq!(event.task_id, "t1");
322    }
323
324    #[test]
325    fn test_bidding_started_event_creation() {
326        let event = BiddingStartedEvent {
327            task_id: "t1".to_string(),
328            started_at: 100,
329        };
330        assert_eq!(event.task_id, "t1");
331        assert_eq!(event.started_at, 100);
332    }
333
334    #[test]
335    fn test_bid_submitted_event_creation() {
336        let score = BidScore::new(0.8, 0.6, 0.9);
337        let event = BidSubmittedEvent {
338            task_id: "t1".to_string(),
339            member_id: "alice".to_string(),
340            score,
341        };
342        assert_eq!(event.task_id, "t1");
343        assert_eq!(event.member_id, "alice");
344    }
345
346    #[test]
347    fn test_bid_rejected_event_creation() {
348        let event = BidRejectedEvent {
349            task_id: "t1".to_string(),
350            member_id: "alice".to_string(),
351            reason: "Insufficient skill level".to_string(),
352        };
353        assert_eq!(event.task_id, "t1");
354        assert_eq!(event.reason, "Insufficient skill level");
355    }
356
357    #[test]
358    fn test_task_assigned_event_creation() {
359        let event = TaskAssignedEvent {
360            task_id: "t1".to_string(),
361            member_id: "alice".to_string(),
362        };
363        assert_eq!(event.task_id, "t1");
364        assert_eq!(event.member_id, "alice");
365    }
366
367    #[test]
368    fn test_task_completed_event_creation() {
369        let event = TaskCompletedEvent {
370            task_id: "t1".to_string(),
371            completed_by: "alice".to_string(),
372            completed_at: 150,
373        };
374        assert_eq!(event.task_id, "t1");
375        assert_eq!(event.completed_by, "alice");
376        assert_eq!(event.completed_at, 150);
377    }
378
379    #[test]
380    fn test_role_assigned_event_creation() {
381        let event = RoleAssignedEvent {
382            circle_id: "c1".to_string(),
383            role_id: "r1".to_string(),
384            member_id: "alice".to_string(),
385        };
386        assert_eq!(event.circle_id, "c1");
387        assert_eq!(event.role_id, "r1");
388        assert_eq!(event.member_id, "alice");
389    }
390
391    #[test]
392    fn test_circle_created_event_creation() {
393        let event = CircleCreatedEvent {
394            circle_id: "c1".to_string(),
395        };
396        assert_eq!(event.circle_id, "c1");
397    }
398
399    #[test]
400    fn test_bidding_completed_event_creation() {
401        let event = BiddingCompletedEvent {
402            task_id: "t1".to_string(),
403            assigned_to: Some("alice".to_string()),
404            bid_count: 3,
405        };
406        assert_eq!(event.task_id, "t1");
407        assert_eq!(event.assigned_to, Some("alice".to_string()));
408        assert_eq!(event.bid_count, 3);
409    }
410}