actionqueue_actor/
department.rs1use std::collections::{HashMap, HashSet};
4
5use actionqueue_core::ids::{ActorId, DepartmentId};
6
7#[derive(Default)]
13pub struct DepartmentRegistry {
14 departments: HashMap<DepartmentId, HashSet<ActorId>>,
15 actor_departments: HashMap<ActorId, DepartmentId>,
17}
18
19impl DepartmentRegistry {
20 pub fn new() -> Self {
22 Self::default()
23 }
24
25 pub fn assign(&mut self, actor_id: ActorId, dept: DepartmentId) {
28 if let Some(prev_dept) = self.actor_departments.remove(&actor_id) {
30 if let Some(members) = self.departments.get_mut(&prev_dept) {
31 members.remove(&actor_id);
32 }
33 }
34 self.departments.entry(dept.clone()).or_default().insert(actor_id);
35 self.actor_departments.insert(actor_id, dept);
36 }
37
38 pub fn remove(&mut self, actor_id: ActorId) {
40 if let Some(dept) = self.actor_departments.remove(&actor_id) {
41 if let Some(members) = self.departments.get_mut(&dept) {
42 members.remove(&actor_id);
43 }
44 }
45 }
46
47 pub fn actors_in_department(&self, dept: &DepartmentId) -> &HashSet<ActorId> {
49 self.departments.get(dept).map_or(&EMPTY_SET, |s| s)
50 }
51
52 pub fn department_of(&self, actor_id: ActorId) -> Option<&DepartmentId> {
54 self.actor_departments.get(&actor_id)
55 }
56}
57
58static EMPTY_SET: std::sync::LazyLock<HashSet<ActorId>> = std::sync::LazyLock::new(HashSet::new);
59
60#[cfg(test)]
61mod tests {
62 use actionqueue_core::ids::{ActorId, DepartmentId};
63
64 use super::DepartmentRegistry;
65
66 fn dept(name: &str) -> DepartmentId {
67 DepartmentId::new(name).unwrap()
68 }
69
70 #[test]
71 fn assign_and_lookup() {
72 let mut reg = DepartmentRegistry::new();
73 let actor = ActorId::new();
74 let engineering = dept("engineering");
75 reg.assign(actor, engineering.clone());
76 assert!(reg.actors_in_department(&engineering).contains(&actor));
77 assert_eq!(reg.department_of(actor), Some(&engineering));
78 }
79
80 #[test]
81 fn reassign_moves_actor() {
82 let mut reg = DepartmentRegistry::new();
83 let actor = ActorId::new();
84 let eng = dept("engineering");
85 let ops = dept("ops");
86 reg.assign(actor, eng.clone());
87 reg.assign(actor, ops.clone());
88 assert!(!reg.actors_in_department(&eng).contains(&actor));
89 assert!(reg.actors_in_department(&ops).contains(&actor));
90 assert_eq!(reg.department_of(actor), Some(&ops));
91 }
92
93 #[test]
94 fn remove_clears_actor() {
95 let mut reg = DepartmentRegistry::new();
96 let actor = ActorId::new();
97 let eng = dept("engineering");
98 reg.assign(actor, eng.clone());
99 reg.remove(actor);
100 assert!(!reg.actors_in_department(&eng).contains(&actor));
101 assert!(reg.department_of(actor).is_none());
102 }
103
104 #[test]
105 fn empty_department_returns_empty_set() {
106 let reg = DepartmentRegistry::new();
107 let eng = dept("engineering");
108 assert!(reg.actors_in_department(&eng).is_empty());
109 }
110}