1use std::sync::Arc;
22
23use chrono::Utc;
24use marshal_entities::{
25 AutoSource, GetAllRoomMembers, GetAllRooms, Room, RoomId, RoomKind, RoomMember, RoomMemberId,
26 Session,
27};
28use myko::{
29 command::{CommandContext, CommandError, CommandHandler},
30 myko_command,
31 prelude::myko_saga,
32 saga::{SagaContext, SagaHandler},
33 wire::{MEvent, MEventType},
34};
35
36pub fn link() {}
39
40#[myko_saga]
43pub struct AutoRoomSaga;
44
45impl SagaHandler for AutoRoomSaga {
46 type EventItem = Session;
47 type Command = DispatchAutoRooms;
48 const EVENT_TYPE: MEventType = MEventType::SET;
49
50 fn handle(session: Session, _event: MEvent, _ctx: Arc<SagaContext>) -> Option<Self::Command> {
51 Some(DispatchAutoRooms {
52 session_id: session.id.0.as_ref().to_string(),
53 })
54 }
55}
56
57#[myko_command]
64pub struct DispatchAutoRooms {
65 pub session_id: String,
66}
67
68impl CommandHandler for DispatchAutoRooms {
69 fn execute(self, ctx: CommandContext) -> Result<(), CommandError> {
70 let sessions: Vec<Arc<Session>> = ctx.exec_query(marshal_entities::GetAllSessions {})?;
74 let Some(session) = sessions
75 .iter()
76 .find(|s| s.id.0.as_ref() == self.session_id.as_str())
77 .cloned()
78 else {
79 return Ok(());
80 };
81
82 let rooms: Vec<Arc<Room>> = ctx.exec_query(GetAllRooms {})?;
83 let memberships: Vec<Arc<RoomMember>> = ctx.exec_query(GetAllRoomMembers {})?;
84 let now = Utc::now().timestamp_millis();
85
86 let mut anchors: Vec<(RoomId, String, AutoSource)> = vec![(
88 RoomId(Arc::from("everyone")),
89 "everyone".to_string(),
90 AutoSource::Everyone,
91 )];
92 if let Some(host) = session.host.as_ref()
93 && !host.name.is_empty()
94 {
95 let id = format!("host:{}", host.name);
96 anchors.push((
97 RoomId(Arc::from(id.as_str())),
98 id,
99 AutoSource::Host {
100 name: host.name.clone(),
101 },
102 ));
103 }
104 if let Some(op) = session.operator.as_ref()
105 && !op.is_empty()
106 {
107 let id = format!("op:{op}");
108 anchors.push((
109 RoomId(Arc::from(id.as_str())),
110 id,
111 AutoSource::Operator { name: op.clone() },
112 ));
113 }
114 if let Some(project) = session.project.as_ref()
115 && !project.is_empty()
116 {
117 let id = format!("project:{project}");
118 anchors.push((
119 RoomId(Arc::from(id.as_str())),
120 id,
121 AutoSource::Project {
122 basename: project.clone(),
123 },
124 ));
125 }
126
127 for (room_id, name, source) in anchors {
128 let already_room = rooms.iter().any(|r| r.id == room_id);
130 if !already_room {
131 ctx.emit_set(&Room {
132 id: room_id.clone(),
133 name,
134 description: None,
135 kind: RoomKind::Auto { source },
136 created_at: now,
137 })?;
138 }
139
140 let member_id = RoomMember::make_id(room_id.0.as_ref(), session.id.0.as_ref());
142 let already_member = memberships
143 .iter()
144 .any(|m| m.id.0.as_ref() == member_id.as_str());
145 if !already_member {
146 ctx.emit_set(&RoomMember {
147 id: RoomMemberId(Arc::from(member_id.as_str())),
148 room_id: room_id.clone(),
149 session_id: session.id.clone(),
150 joined_at: now,
151 })?;
152 }
153 }
154
155 Ok(())
156 }
157}