1use serde::de::{self, MapAccess, SeqAccess, Visitor};
2use serde::{Deserialize, Deserializer, Serialize};
3use std::path::PathBuf;
4
5use crate::{SessionName, WindowTarget};
6
7#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
9pub struct NewWindowRequest {
10 pub target: SessionName,
12 pub name: Option<String>,
14 pub detached: bool,
16 #[serde(default)]
18 pub environment: Option<Vec<String>>,
19 #[serde(default)]
21 pub command: Option<Vec<String>>,
22 #[serde(default)]
24 pub start_directory: Option<PathBuf>,
25 #[serde(default)]
27 pub target_window_index: Option<u32>,
28 #[serde(default)]
30 pub insert_at_target: bool,
31}
32
33#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
35pub struct KillWindowRequest {
36 pub target: WindowTarget,
38 pub kill_all_others: bool,
40}
41
42#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
44pub struct SelectWindowRequest {
45 pub target: WindowTarget,
47}
48
49#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
51pub struct RenameWindowRequest {
52 pub target: WindowTarget,
54 pub name: String,
56}
57
58#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
60pub struct NextWindowRequest {
61 pub target: SessionName,
63 #[serde(default)]
65 pub alerts_only: bool,
66}
67
68#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
70pub struct PreviousWindowRequest {
71 pub target: SessionName,
73 #[serde(default)]
75 pub alerts_only: bool,
76}
77
78#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
80pub struct LastWindowRequest {
81 pub target: SessionName,
83}
84
85#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
87pub struct ListWindowsRequest {
88 pub target: SessionName,
90 pub format: Option<String>,
92}
93
94#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
96pub struct LinkWindowRequest {
97 pub source: WindowTarget,
99 pub target: WindowTarget,
101 #[serde(default)]
103 pub after: bool,
104 #[serde(default)]
106 pub before: bool,
107 #[serde(default)]
109 pub kill_destination: bool,
110 #[serde(default)]
112 pub detached: bool,
113}
114
115#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
117pub enum MoveWindowTarget {
118 Session(SessionName),
120 Window(WindowTarget),
122}
123
124#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
126pub struct MoveWindowRequest {
127 pub source: Option<WindowTarget>,
129 pub target: MoveWindowTarget,
131 pub renumber: bool,
133 pub kill_destination: bool,
135 pub detached: bool,
137}
138
139#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
141pub struct SwapWindowRequest {
142 pub source: WindowTarget,
144 pub target: WindowTarget,
146 pub detached: bool,
148}
149
150#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
152pub enum RotateWindowDirection {
153 Down,
155 Up,
157}
158
159#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
161pub struct RotateWindowRequest {
162 pub target: WindowTarget,
164 pub direction: RotateWindowDirection,
166 #[serde(default)]
168 pub restore_zoom: bool,
169}
170
171#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
173pub struct ResizeWindowRequest {
174 pub target: WindowTarget,
176 pub width: Option<u16>,
178 pub height: Option<u16>,
180 #[serde(default)]
182 pub adjustment: Option<ResizeWindowAdjustment>,
183}
184
185#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
187pub enum ResizeWindowAdjustment {
188 Up(u16),
190 Down(u16),
192 Left(u16),
194 Right(u16),
196}
197
198#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
200pub struct RespawnWindowRequest {
201 pub target: WindowTarget,
203 #[serde(default)]
205 pub kill: bool,
206 #[serde(default)]
208 pub environment: Option<Vec<String>>,
209 #[serde(default)]
211 pub command: Option<Vec<String>>,
212 #[serde(default)]
214 pub start_directory: Option<PathBuf>,
215}
216
217impl<'de> Deserialize<'de> for NewWindowRequest {
218 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
219 where
220 D: Deserializer<'de>,
221 {
222 deserializer.deserialize_struct(
223 "NewWindowRequest",
224 &[
225 "target",
226 "name",
227 "detached",
228 "environment",
229 "command",
230 "start_directory",
231 "target_window_index",
232 "insert_at_target",
233 ],
234 NewWindowRequestVisitor,
235 )
236 }
237}
238
239impl<'de> Deserialize<'de> for RespawnWindowRequest {
240 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
241 where
242 D: Deserializer<'de>,
243 {
244 deserializer.deserialize_struct(
245 "RespawnWindowRequest",
246 &[
247 "target",
248 "kill",
249 "environment",
250 "command",
251 "start_directory",
252 ],
253 RespawnWindowRequestVisitor,
254 )
255 }
256}
257
258struct NewWindowRequestVisitor;
259
260impl<'de> Visitor<'de> for NewWindowRequestVisitor {
261 type Value = NewWindowRequest;
262
263 fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
264 formatter.write_str("a new-window request")
265 }
266
267 fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
268 where
269 A: SeqAccess<'de>,
270 {
271 let target = seq
272 .next_element()?
273 .ok_or_else(|| de::Error::invalid_length(0, &self))?;
274 let name = seq
275 .next_element()?
276 .ok_or_else(|| de::Error::invalid_length(1, &self))?;
277 let detached = seq
278 .next_element()?
279 .ok_or_else(|| de::Error::invalid_length(2, &self))?;
280 let environment = seq
281 .next_element()?
282 .ok_or_else(|| de::Error::invalid_length(3, &self))?;
283 let command = compat_next_element(&mut seq)?;
284 let start_directory = compat_next_element(&mut seq)?;
285 let target_window_index = compat_next_element(&mut seq)?;
286 let insert_at_target = compat_next_element(&mut seq)?;
287
288 Ok(NewWindowRequest {
289 target,
290 name,
291 detached,
292 environment,
293 command,
294 start_directory,
295 target_window_index,
296 insert_at_target,
297 })
298 }
299
300 fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
301 where
302 A: MapAccess<'de>,
303 {
304 let mut target = None;
305 let mut name = None;
306 let mut detached = None;
307 let mut environment = None;
308 let mut command = None;
309 let mut start_directory = None;
310 let mut target_window_index = None;
311 let mut insert_at_target = None;
312
313 while let Some(key) = map.next_key::<String>()? {
314 match key.as_str() {
315 "target" => target = Some(map.next_value()?),
316 "name" => name = Some(map.next_value()?),
317 "detached" => detached = Some(map.next_value()?),
318 "environment" => environment = Some(map.next_value()?),
319 "command" => command = Some(map.next_value()?),
320 "start_directory" => start_directory = Some(map.next_value()?),
321 "target_window_index" => target_window_index = Some(map.next_value()?),
322 "insert_at_target" => insert_at_target = Some(map.next_value()?),
323 _ => {
324 let _: de::IgnoredAny = map.next_value()?;
325 }
326 }
327 }
328
329 Ok(NewWindowRequest {
330 target: target.ok_or_else(|| de::Error::missing_field("target"))?,
331 name: name.ok_or_else(|| de::Error::missing_field("name"))?,
332 detached: detached.ok_or_else(|| de::Error::missing_field("detached"))?,
333 environment: environment.ok_or_else(|| de::Error::missing_field("environment"))?,
334 command: command.unwrap_or_default(),
335 start_directory: start_directory.unwrap_or_default(),
336 target_window_index: target_window_index.unwrap_or_default(),
337 insert_at_target: insert_at_target.unwrap_or_default(),
338 })
339 }
340}
341
342struct RespawnWindowRequestVisitor;
343
344impl<'de> Visitor<'de> for RespawnWindowRequestVisitor {
345 type Value = RespawnWindowRequest;
346
347 fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
348 formatter.write_str("a respawn-window request")
349 }
350
351 fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
352 where
353 A: SeqAccess<'de>,
354 {
355 let target = seq
356 .next_element()?
357 .ok_or_else(|| de::Error::invalid_length(0, &self))?;
358 let kill = seq
359 .next_element()?
360 .ok_or_else(|| de::Error::invalid_length(1, &self))?;
361 let environment = seq
362 .next_element()?
363 .ok_or_else(|| de::Error::invalid_length(2, &self))?;
364 let command = compat_next_element(&mut seq)?;
365 let start_directory = compat_next_element(&mut seq)?;
366
367 Ok(RespawnWindowRequest {
368 target,
369 kill,
370 environment,
371 command,
372 start_directory,
373 })
374 }
375
376 fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
377 where
378 A: MapAccess<'de>,
379 {
380 let mut target = None;
381 let mut kill = None;
382 let mut environment = None;
383 let mut command = None;
384 let mut start_directory = None;
385
386 while let Some(key) = map.next_key::<String>()? {
387 match key.as_str() {
388 "target" => target = Some(map.next_value()?),
389 "kill" => kill = Some(map.next_value()?),
390 "environment" => environment = Some(map.next_value()?),
391 "command" => command = Some(map.next_value()?),
392 "start_directory" => start_directory = Some(map.next_value()?),
393 _ => {
394 let _: de::IgnoredAny = map.next_value()?;
395 }
396 }
397 }
398
399 Ok(RespawnWindowRequest {
400 target: target.ok_or_else(|| de::Error::missing_field("target"))?,
401 kill: kill.ok_or_else(|| de::Error::missing_field("kill"))?,
402 environment: environment.ok_or_else(|| de::Error::missing_field("environment"))?,
403 command: command.unwrap_or_default(),
404 start_directory: start_directory.unwrap_or_default(),
405 })
406 }
407}
408
409fn compat_next_element<'de, A, T>(seq: &mut A) -> Result<T, A::Error>
410where
411 A: SeqAccess<'de>,
412 T: Deserialize<'de> + Default,
413{
414 match seq.next_element::<T>() {
415 Ok(Some(value)) => Ok(value),
416 Ok(None) => Ok(T::default()),
417 Err(error) if is_truncated_compat_sequence(&error) => Ok(T::default()),
418 Err(error) => Err(error),
419 }
420}
421
422fn is_truncated_compat_sequence(error: &impl std::fmt::Display) -> bool {
423 let message = error.to_string();
424 message.contains("UnexpectedEof") || message.contains("unexpected end of file")
425}