rmux_core/session/
accessors.rs1use std::collections::BTreeMap;
2use std::path::{Path, PathBuf};
3
4use rmux_proto::{SessionName, TerminalSize};
5
6use super::{current_unix_timestamp, synchronized_active_window, Session, WindowIdAllocator};
7use crate::{AlertFlags, Pane, PaneId, SessionId, Window, WINLINK_ALERTFLAGS};
8
9impl Session {
10 #[must_use]
12 pub const fn name(&self) -> &SessionName {
13 &self.name
14 }
15
16 #[must_use]
18 pub const fn group_name(&self) -> Option<&SessionName> {
19 self.group_name.as_ref()
20 }
21
22 #[must_use]
24 pub const fn id(&self) -> SessionId {
25 self.id
26 }
27
28 #[must_use]
30 pub const fn created_at(&self) -> i64 {
31 self.created_at
32 }
33
34 #[must_use]
36 pub const fn activity_at(&self) -> i64 {
37 self.activity_at
38 }
39
40 #[must_use]
42 pub const fn last_attached_at(&self) -> Option<i64> {
43 self.last_attached_at
44 }
45
46 #[must_use]
48 pub fn cwd(&self) -> Option<&Path> {
49 self.cwd.as_deref()
50 }
51
52 #[must_use]
54 pub const fn terminal_size(&self) -> TerminalSize {
55 self.terminal_size
56 }
57
58 pub(crate) fn set_id(&mut self, id: SessionId) {
59 self.id = id;
60 }
61
62 pub(crate) fn rebind_window_id_allocator(&mut self, allocator: WindowIdAllocator) {
63 let next_after_windows = self
64 .windows
65 .values()
66 .map(|window| window.id().as_u32().saturating_add(1))
67 .max()
68 .unwrap_or_else(|| allocator.peek());
69 self.next_window_id = allocator;
70 self.next_window_id.bump_to(next_after_windows);
71 }
72
73 pub fn rename(&mut self, new_name: SessionName) {
75 self.name = new_name;
76 }
77
78 pub fn set_group_name(&mut self, group_name: Option<SessionName>) {
80 self.group_name = group_name;
81 }
82
83 pub fn set_cwd(&mut self, cwd: Option<PathBuf>) {
85 self.cwd = cwd;
86 }
87
88 pub fn touch_activity(&mut self) {
90 self.activity_at = current_unix_timestamp();
91 }
92
93 pub fn touch_attached(&mut self) {
95 let now = current_unix_timestamp();
96 self.activity_at = now;
97 self.last_attached_at = Some(now);
98 }
99
100 #[must_use]
102 pub fn clone_as_group_member(
103 &self,
104 name: SessionName,
105 group_name: SessionName,
106 session_id: SessionId,
107 ) -> Self {
108 let now = current_unix_timestamp();
109 let mut cloned = self.clone();
110 cloned.id = session_id;
111 cloned.name = name;
112 cloned.group_name = Some(group_name);
113 cloned.created_at = now;
114 cloned.activity_at = now;
115 cloned.last_attached_at = None;
116 cloned
117 }
118
119 pub fn synchronize_group_from(&mut self, source: &Session) {
121 debug_assert_ne!(self.name, source.name);
122 let previous_active = self.active_window;
123 let previous_last = self.last_window;
124
125 self.windows = source.windows.clone();
126 self.winlink_alert_flags = source.winlink_alert_flags.clone();
127 self.next_pane_id = source.next_pane_id;
128 self.cwd = source.cwd.clone();
129
130 self.active_window =
131 synchronized_active_window(&self.windows, previous_active, previous_last);
132 self.last_window = previous_last
133 .filter(|window_index| {
134 *window_index != self.active_window && self.windows.contains_key(window_index)
135 })
136 .or_else(|| {
137 (previous_active != self.active_window
138 && self.windows.contains_key(&previous_active))
139 .then_some(previous_active)
140 });
141 }
142
143 #[must_use]
145 pub fn window(&self) -> &Window {
146 self.window_at(self.active_window)
147 .expect("active session window must exist")
148 }
149
150 #[must_use]
152 pub fn window_at(&self, window_index: u32) -> Option<&Window> {
153 self.windows.get(&window_index)
154 }
155
156 #[must_use]
158 pub const fn windows(&self) -> &BTreeMap<u32, Window> {
159 &self.windows
160 }
161
162 pub(crate) fn window_mut(&mut self) -> &mut Window {
163 self.window_at_mut(self.active_window)
164 .expect("active session window must exist")
165 }
166
167 pub fn window_at_mut(&mut self, window_index: u32) -> Option<&mut Window> {
169 self.windows.get_mut(&window_index)
170 }
171
172 #[must_use]
174 pub fn winlink_alert_flags(&self, window_index: u32) -> AlertFlags {
175 self.winlink_alert_flags
176 .get(&window_index)
177 .copied()
178 .unwrap_or_else(AlertFlags::empty)
179 }
180
181 #[must_use]
183 pub fn session_alert_flags(&self) -> AlertFlags {
184 self.winlink_alert_flags
185 .values()
186 .copied()
187 .fold(AlertFlags::empty(), |flags, winlink_flags| {
188 flags.union(winlink_flags)
189 })
190 }
191
192 #[must_use]
194 pub fn alerted_window_indexes(&self) -> Vec<u32> {
195 self.winlink_alert_flags
196 .iter()
197 .filter_map(|(window_index, flags)| {
198 flags
199 .intersects(WINLINK_ALERTFLAGS)
200 .then_some(*window_index)
201 })
202 .collect()
203 }
204
205 #[must_use]
207 pub fn has_alerts(&self) -> bool {
208 self.winlink_alert_flags
209 .values()
210 .any(|flags| flags.intersects(WINLINK_ALERTFLAGS))
211 }
212
213 pub fn add_winlink_alert_flags(&mut self, window_index: u32, flags: AlertFlags) -> bool {
215 if !self.windows.contains_key(&window_index) {
216 return false;
217 }
218
219 let entry = self
220 .winlink_alert_flags
221 .entry(window_index)
222 .or_insert_with(AlertFlags::empty);
223 let changed = !entry.contains(flags);
224 entry.insert(flags);
225 changed
226 }
227
228 pub fn clear_winlink_alert_flags(&mut self, window_index: u32, flags: AlertFlags) -> bool {
230 let Some(entry) = self.winlink_alert_flags.get_mut(&window_index) else {
231 return false;
232 };
233 if !entry.intersects(flags) {
234 return false;
235 }
236
237 entry.remove(flags);
238 true
239 }
240
241 pub fn clear_all_winlink_alert_flags(&mut self, window_index: u32) -> bool {
243 self.clear_winlink_alert_flags(window_index, WINLINK_ALERTFLAGS)
244 }
245
246 #[must_use]
248 pub const fn active_window_index(&self) -> u32 {
249 self.active_window
250 }
251
252 #[must_use]
254 pub const fn last_window_index(&self) -> Option<u32> {
255 self.last_window
256 }
257
258 #[must_use]
260 pub fn active_pane_index(&self) -> u32 {
261 self.window().active_pane_index()
262 }
263
264 #[must_use]
266 pub fn active_pane_id(&self) -> Option<PaneId> {
267 self.active_pane().map(Pane::id)
268 }
269
270 #[must_use]
272 pub fn active_pane(&self) -> Option<&Pane> {
273 self.window().active_pane()
274 }
275
276 #[must_use]
278 pub fn pane_id(&self, pane_index: u32) -> Option<PaneId> {
279 self.pane_id_in_window(self.active_window, pane_index)
280 }
281
282 #[must_use]
284 pub fn pane_id_in_window(&self, window_index: u32, pane_index: u32) -> Option<PaneId> {
285 self.window_at(window_index)
286 .and_then(|window| window.pane_id(pane_index))
287 }
288}