rmux_core/session/
accessors.rs1use std::collections::BTreeMap;
2use std::path::{Path, PathBuf};
3
4use rmux_proto::SessionName;
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 pub(crate) fn set_id(&mut self, id: SessionId) {
53 self.id = id;
54 }
55
56 pub(crate) fn rebind_window_id_allocator(&mut self, allocator: WindowIdAllocator) {
57 let next_after_windows = self
58 .windows
59 .values()
60 .map(|window| window.id().as_u32().saturating_add(1))
61 .max()
62 .unwrap_or_else(|| allocator.peek());
63 self.next_window_id = allocator;
64 self.next_window_id.bump_to(next_after_windows);
65 }
66
67 pub fn rename(&mut self, new_name: SessionName) {
69 self.name = new_name;
70 }
71
72 pub fn set_group_name(&mut self, group_name: Option<SessionName>) {
74 self.group_name = group_name;
75 }
76
77 pub fn set_cwd(&mut self, cwd: Option<PathBuf>) {
79 self.cwd = cwd;
80 }
81
82 pub fn touch_activity(&mut self) {
84 self.activity_at = current_unix_timestamp();
85 }
86
87 pub fn touch_attached(&mut self) {
89 let now = current_unix_timestamp();
90 self.activity_at = now;
91 self.last_attached_at = Some(now);
92 }
93
94 #[must_use]
96 pub fn clone_as_group_member(
97 &self,
98 name: SessionName,
99 group_name: SessionName,
100 session_id: SessionId,
101 ) -> Self {
102 let now = current_unix_timestamp();
103 let mut cloned = self.clone();
104 cloned.id = session_id;
105 cloned.name = name;
106 cloned.group_name = Some(group_name);
107 cloned.created_at = now;
108 cloned.activity_at = now;
109 cloned.last_attached_at = None;
110 cloned
111 }
112
113 pub fn synchronize_group_from(&mut self, source: &Session) {
115 debug_assert_ne!(self.name, source.name);
116 let previous_active = self.active_window;
117 let previous_last = self.last_window;
118
119 self.windows = source.windows.clone();
120 self.winlink_alert_flags = source.winlink_alert_flags.clone();
121 self.next_pane_id = source.next_pane_id;
122 self.cwd = source.cwd.clone();
123
124 self.active_window =
125 synchronized_active_window(&self.windows, previous_active, previous_last);
126 self.last_window = previous_last
127 .filter(|window_index| {
128 *window_index != self.active_window && self.windows.contains_key(window_index)
129 })
130 .or_else(|| {
131 (previous_active != self.active_window
132 && self.windows.contains_key(&previous_active))
133 .then_some(previous_active)
134 });
135 }
136
137 #[must_use]
139 pub fn window(&self) -> &Window {
140 self.window_at(self.active_window)
141 .expect("active session window must exist")
142 }
143
144 #[must_use]
146 pub fn window_at(&self, window_index: u32) -> Option<&Window> {
147 self.windows.get(&window_index)
148 }
149
150 #[must_use]
152 pub const fn windows(&self) -> &BTreeMap<u32, Window> {
153 &self.windows
154 }
155
156 pub(crate) fn window_mut(&mut self) -> &mut Window {
157 self.window_at_mut(self.active_window)
158 .expect("active session window must exist")
159 }
160
161 pub fn window_at_mut(&mut self, window_index: u32) -> Option<&mut Window> {
163 self.windows.get_mut(&window_index)
164 }
165
166 #[must_use]
168 pub fn winlink_alert_flags(&self, window_index: u32) -> AlertFlags {
169 self.winlink_alert_flags
170 .get(&window_index)
171 .copied()
172 .unwrap_or_else(AlertFlags::empty)
173 }
174
175 #[must_use]
177 pub fn session_alert_flags(&self) -> AlertFlags {
178 self.winlink_alert_flags
179 .values()
180 .copied()
181 .fold(AlertFlags::empty(), |flags, winlink_flags| {
182 flags.union(winlink_flags)
183 })
184 }
185
186 #[must_use]
188 pub fn alerted_window_indexes(&self) -> Vec<u32> {
189 self.winlink_alert_flags
190 .iter()
191 .filter_map(|(window_index, flags)| {
192 flags
193 .intersects(WINLINK_ALERTFLAGS)
194 .then_some(*window_index)
195 })
196 .collect()
197 }
198
199 #[must_use]
201 pub fn has_alerts(&self) -> bool {
202 self.winlink_alert_flags
203 .values()
204 .any(|flags| flags.intersects(WINLINK_ALERTFLAGS))
205 }
206
207 pub fn add_winlink_alert_flags(&mut self, window_index: u32, flags: AlertFlags) -> bool {
209 if !self.windows.contains_key(&window_index) {
210 return false;
211 }
212
213 let entry = self
214 .winlink_alert_flags
215 .entry(window_index)
216 .or_insert_with(AlertFlags::empty);
217 let changed = !entry.contains(flags);
218 entry.insert(flags);
219 changed
220 }
221
222 pub fn clear_winlink_alert_flags(&mut self, window_index: u32, flags: AlertFlags) -> bool {
224 let Some(entry) = self.winlink_alert_flags.get_mut(&window_index) else {
225 return false;
226 };
227 if !entry.intersects(flags) {
228 return false;
229 }
230
231 entry.remove(flags);
232 true
233 }
234
235 pub fn clear_all_winlink_alert_flags(&mut self, window_index: u32) -> bool {
237 self.clear_winlink_alert_flags(window_index, WINLINK_ALERTFLAGS)
238 }
239
240 #[must_use]
242 pub const fn active_window_index(&self) -> u32 {
243 self.active_window
244 }
245
246 #[must_use]
248 pub const fn last_window_index(&self) -> Option<u32> {
249 self.last_window
250 }
251
252 #[must_use]
254 pub fn active_pane_index(&self) -> u32 {
255 self.window().active_pane_index()
256 }
257
258 #[must_use]
260 pub fn active_pane_id(&self) -> Option<PaneId> {
261 self.active_pane().map(Pane::id)
262 }
263
264 #[must_use]
266 pub fn active_pane(&self) -> Option<&Pane> {
267 self.window().active_pane()
268 }
269
270 #[must_use]
272 pub fn pane_id(&self, pane_index: u32) -> Option<PaneId> {
273 self.pane_id_in_window(self.active_window, pane_index)
274 }
275
276 #[must_use]
278 pub fn pane_id_in_window(&self, window_index: u32, pane_index: u32) -> Option<PaneId> {
279 self.window_at(window_index)
280 .and_then(|window| window.pane_id(pane_index))
281 }
282}