1use std::collections::{BTreeMap, HashMap};
2
3use rmux_proto::{PaneTarget, RmuxError, SessionName, WindowTarget};
4
5use super::{HookBindings, HookStore};
6
7impl HookStore {
8 pub fn remove_session(&mut self, session_name: &SessionName) -> bool {
10 let mut removed = self.sessions.remove(session_name).is_some();
11 self.window_aliases
12 .retain(|target, _| target.session_name() != session_name);
13 self.pane_aliases
14 .retain(|target, _| target.session_name() != session_name);
15 self.windows.retain(|target, _| {
16 let keep = target.session_name() != session_name;
17 removed |= !keep;
18 keep
19 });
20 self.panes.retain(|target, _| {
21 let keep = target.session_name() != session_name;
22 removed |= !keep;
23 keep
24 });
25 removed
26 }
27
28 pub fn remove_window(&mut self, target: &WindowTarget) -> bool {
30 let mut removed = self.windows.remove(target).is_some();
31 self.window_aliases.remove(target);
32 self.pane_aliases.retain(|pane_target, _| {
33 pane_target.session_name() != target.session_name()
34 || pane_target.window_index() != target.window_index()
35 });
36 self.panes.retain(|pane_target, _| {
37 let keep = pane_target.session_name() != target.session_name()
38 || pane_target.window_index() != target.window_index();
39 removed |= !keep;
40 keep
41 });
42 removed
43 }
44
45 pub fn swap_window_hooks(&mut self, source: &WindowTarget, target: &WindowTarget) {
47 if source == target {
48 return;
49 }
50
51 let source_window = self.windows.remove(source);
52 let target_window = self.windows.remove(target);
53 if let Some(bindings) = source_window {
54 self.windows.insert(target.clone(), bindings);
55 }
56 if let Some(bindings) = target_window {
57 self.windows.insert(source.clone(), bindings);
58 }
59
60 let source_panes = remove_window_pane_hooks(&mut self.panes, source);
61 let target_panes = remove_window_pane_hooks(&mut self.panes, target);
62 self.panes
63 .extend(rekey_pane_hooks(source_panes, source, target));
64 self.panes
65 .extend(rekey_pane_hooks(target_panes, target, source));
66 }
67
68 pub fn move_window_hooks(&mut self, source: &WindowTarget, target: &WindowTarget) {
70 if source == target {
71 return;
72 }
73
74 let source_window = self.windows.remove(source);
75 let _ = self.windows.remove(target);
76 if let Some(bindings) = source_window {
77 self.windows.insert(target.clone(), bindings);
78 }
79
80 let source_panes = remove_window_pane_hooks(&mut self.panes, source);
81 let _ = remove_window_pane_hooks(&mut self.panes, target);
82 self.panes
83 .extend(rekey_pane_hooks(source_panes, source, target));
84 }
85
86 pub fn remove_pane(&mut self, target: &PaneTarget) -> bool {
88 self.pane_aliases.remove(target);
89 self.panes.remove(target).is_some()
90 }
91
92 pub fn remap_session_window_indices(
94 &mut self,
95 session_name: &SessionName,
96 index_map: &BTreeMap<u32, u32>,
97 ) -> Result<(), RmuxError> {
98 let mut remapped_windows = HashMap::with_capacity(self.windows.len());
99 for (target, bindings) in &self.windows {
100 let next_target = remapped_window_target(target, session_name, index_map);
101 if remapped_windows
102 .insert(next_target.clone(), bindings.clone())
103 .is_some()
104 {
105 return Err(RmuxError::Server(format!(
106 "hooks already exist for {next_target}"
107 )));
108 }
109 }
110
111 let mut remapped_panes = HashMap::with_capacity(self.panes.len());
112 for (target, bindings) in &self.panes {
113 let next_target = remapped_pane_target(target, session_name, index_map);
114 if remapped_panes
115 .insert(next_target.clone(), bindings.clone())
116 .is_some()
117 {
118 return Err(RmuxError::Server(format!(
119 "hooks already exist for {next_target}"
120 )));
121 }
122 }
123
124 self.windows = remapped_windows;
125 self.panes = remapped_panes;
126 Ok(())
127 }
128
129 pub fn rename_session(
131 &mut self,
132 session_name: &SessionName,
133 new_name: SessionName,
134 ) -> Result<(), RmuxError> {
135 let mut renamed_sessions = HashMap::with_capacity(self.sessions.len());
136 for (name, bindings) in &self.sessions {
137 let next_name = if name == session_name {
138 new_name.clone()
139 } else {
140 name.clone()
141 };
142 if renamed_sessions
143 .insert(next_name.clone(), bindings.clone())
144 .is_some()
145 {
146 return Err(RmuxError::Server(format!(
147 "hooks already exist for session {next_name}"
148 )));
149 }
150 }
151
152 let mut renamed_windows = HashMap::with_capacity(self.windows.len());
153 for (target, bindings) in &self.windows {
154 let next_target = if target.session_name() == session_name {
155 WindowTarget::with_window(new_name.clone(), target.window_index())
156 } else {
157 target.clone()
158 };
159 if renamed_windows
160 .insert(next_target.clone(), bindings.clone())
161 .is_some()
162 {
163 return Err(RmuxError::Server(format!(
164 "hooks already exist for {next_target}"
165 )));
166 }
167 }
168
169 let mut renamed_panes = HashMap::with_capacity(self.panes.len());
170 for (target, bindings) in &self.panes {
171 let next_target = if target.session_name() == session_name {
172 PaneTarget::with_window(
173 new_name.clone(),
174 target.window_index(),
175 target.pane_index(),
176 )
177 } else {
178 target.clone()
179 };
180 if renamed_panes
181 .insert(next_target.clone(), bindings.clone())
182 .is_some()
183 {
184 return Err(RmuxError::Server(format!(
185 "hooks already exist for {next_target}"
186 )));
187 }
188 }
189
190 self.sessions = renamed_sessions;
191 self.windows = renamed_windows;
192 self.panes = renamed_panes;
193 Ok(())
194 }
195}
196
197fn remove_window_pane_hooks(
198 panes: &mut HashMap<PaneTarget, HookBindings>,
199 window: &WindowTarget,
200) -> Vec<(PaneTarget, HookBindings)> {
201 let pane_targets = panes
202 .keys()
203 .filter(|pane_target| {
204 pane_target.session_name() == window.session_name()
205 && pane_target.window_index() == window.window_index()
206 })
207 .cloned()
208 .collect::<Vec<_>>();
209 pane_targets
210 .into_iter()
211 .filter_map(|pane_target| {
212 panes
213 .remove(&pane_target)
214 .map(|bindings| (pane_target, bindings))
215 })
216 .collect()
217}
218
219fn rekey_pane_hooks(
220 panes: Vec<(PaneTarget, HookBindings)>,
221 source: &WindowTarget,
222 target: &WindowTarget,
223) -> Vec<(PaneTarget, HookBindings)> {
224 panes
225 .into_iter()
226 .map(move |(pane_target, bindings)| {
227 debug_assert_eq!(pane_target.session_name(), source.session_name());
228 debug_assert_eq!(pane_target.window_index(), source.window_index());
229 (
230 PaneTarget::with_window(
231 target.session_name().clone(),
232 target.window_index(),
233 pane_target.pane_index(),
234 ),
235 bindings,
236 )
237 })
238 .collect()
239}
240
241fn remapped_window_target(
242 target: &WindowTarget,
243 session_name: &SessionName,
244 index_map: &BTreeMap<u32, u32>,
245) -> WindowTarget {
246 if target.session_name() != session_name {
247 return target.clone();
248 }
249 index_map.get(&target.window_index()).copied().map_or_else(
250 || target.clone(),
251 |window_index| WindowTarget::with_window(session_name.clone(), window_index),
252 )
253}
254
255fn remapped_pane_target(
256 target: &PaneTarget,
257 session_name: &SessionName,
258 index_map: &BTreeMap<u32, u32>,
259) -> PaneTarget {
260 if target.session_name() != session_name {
261 return target.clone();
262 }
263 index_map.get(&target.window_index()).copied().map_or_else(
264 || target.clone(),
265 |window_index| {
266 PaneTarget::with_window(session_name.clone(), window_index, target.pane_index())
267 },
268 )
269}