1use serde::de::{self, MapAccess, SeqAccess, Visitor};
2use serde::{Deserialize, Deserializer, Serialize};
3
4use crate::{ProcessCommand, SessionName, TerminalSize};
5
6#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
8pub struct NewSessionRequest {
9 pub session_name: SessionName,
11 pub detached: bool,
13 pub size: Option<TerminalSize>,
15 #[serde(default)]
17 pub environment: Option<Vec<String>>,
18}
19
20#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
22pub struct NewSessionExtRequest {
23 pub session_name: Option<SessionName>,
25 #[serde(default)]
27 pub working_directory: Option<String>,
28 pub detached: bool,
30 pub size: Option<TerminalSize>,
32 #[serde(default)]
34 pub environment: Option<Vec<String>>,
35 #[serde(default)]
37 pub group_target: Option<SessionName>,
38 #[serde(default)]
40 pub attach_if_exists: bool,
41 #[serde(default)]
43 pub detach_other_clients: bool,
44 #[serde(default)]
46 pub kill_other_clients: bool,
47 #[serde(default)]
49 pub flags: Option<Vec<String>>,
50 #[serde(default)]
52 pub window_name: Option<String>,
53 #[serde(default)]
55 pub print_session_info: bool,
56 #[serde(default)]
58 pub print_format: Option<String>,
59 #[serde(default)]
62 pub command: Option<Vec<String>>,
63 #[serde(default)]
65 pub process_command: Option<ProcessCommand>,
66}
67
68impl<'de> Deserialize<'de> for NewSessionExtRequest {
69 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
70 where
71 D: Deserializer<'de>,
72 {
73 deserializer.deserialize_struct(
74 "NewSessionExtRequest",
75 &[
76 "session_name",
77 "working_directory",
78 "detached",
79 "size",
80 "environment",
81 "group_target",
82 "attach_if_exists",
83 "detach_other_clients",
84 "kill_other_clients",
85 "flags",
86 "window_name",
87 "print_session_info",
88 "print_format",
89 "command",
90 "process_command",
91 ],
92 NewSessionExtRequestVisitor,
93 )
94 }
95}
96
97struct NewSessionExtRequestVisitor;
98
99impl<'de> Visitor<'de> for NewSessionExtRequestVisitor {
100 type Value = NewSessionExtRequest;
101
102 fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
103 formatter.write_str("a new-session extended request")
104 }
105
106 fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
107 where
108 A: SeqAccess<'de>,
109 {
110 let session_name = required_next(&mut seq, 0, &self)?;
111 let working_directory = required_next(&mut seq, 1, &self)?;
112 let detached = required_next(&mut seq, 2, &self)?;
113 let size = required_next(&mut seq, 3, &self)?;
114 let environment = required_next(&mut seq, 4, &self)?;
115 let group_target = required_next(&mut seq, 5, &self)?;
116 let attach_if_exists = required_next(&mut seq, 6, &self)?;
117 let detach_other_clients = required_next(&mut seq, 7, &self)?;
118 let kill_other_clients = required_next(&mut seq, 8, &self)?;
119 let flags = required_next(&mut seq, 9, &self)?;
120 let window_name = required_next(&mut seq, 10, &self)?;
121 let print_session_info = required_next(&mut seq, 11, &self)?;
122 let print_format = required_next(&mut seq, 12, &self)?;
123 let command = required_next(&mut seq, 13, &self)?;
124 let process_command = compat_next_element(&mut seq)?;
125
126 Ok(NewSessionExtRequest {
127 session_name,
128 working_directory,
129 detached,
130 size,
131 environment,
132 group_target,
133 attach_if_exists,
134 detach_other_clients,
135 kill_other_clients,
136 flags,
137 window_name,
138 print_session_info,
139 print_format,
140 command,
141 process_command,
142 })
143 }
144
145 fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
146 where
147 A: MapAccess<'de>,
148 {
149 let mut session_name = None;
150 let mut working_directory = None;
151 let mut detached = None;
152 let mut size = None;
153 let mut environment = None;
154 let mut group_target = None;
155 let mut attach_if_exists = None;
156 let mut detach_other_clients = None;
157 let mut kill_other_clients = None;
158 let mut flags = None;
159 let mut window_name = None;
160 let mut print_session_info = None;
161 let mut print_format = None;
162 let mut command = None;
163 let mut process_command = None;
164
165 while let Some(key) = map.next_key::<String>()? {
166 match key.as_str() {
167 "session_name" => session_name = Some(map.next_value()?),
168 "working_directory" => working_directory = Some(map.next_value()?),
169 "detached" => detached = Some(map.next_value()?),
170 "size" => size = Some(map.next_value()?),
171 "environment" => environment = Some(map.next_value()?),
172 "group_target" => group_target = Some(map.next_value()?),
173 "attach_if_exists" => attach_if_exists = Some(map.next_value()?),
174 "detach_other_clients" => detach_other_clients = Some(map.next_value()?),
175 "kill_other_clients" => kill_other_clients = Some(map.next_value()?),
176 "flags" => flags = Some(map.next_value()?),
177 "window_name" => window_name = Some(map.next_value()?),
178 "print_session_info" => print_session_info = Some(map.next_value()?),
179 "print_format" => print_format = Some(map.next_value()?),
180 "command" => command = Some(map.next_value()?),
181 "process_command" => process_command = Some(map.next_value()?),
182 _ => {
183 let _: de::IgnoredAny = map.next_value()?;
184 }
185 }
186 }
187
188 Ok(NewSessionExtRequest {
189 session_name: session_name.ok_or_else(|| de::Error::missing_field("session_name"))?,
190 working_directory: working_directory
191 .ok_or_else(|| de::Error::missing_field("working_directory"))?,
192 detached: detached.ok_or_else(|| de::Error::missing_field("detached"))?,
193 size: size.ok_or_else(|| de::Error::missing_field("size"))?,
194 environment: environment.ok_or_else(|| de::Error::missing_field("environment"))?,
195 group_target: group_target.ok_or_else(|| de::Error::missing_field("group_target"))?,
196 attach_if_exists: attach_if_exists
197 .ok_or_else(|| de::Error::missing_field("attach_if_exists"))?,
198 detach_other_clients: detach_other_clients
199 .ok_or_else(|| de::Error::missing_field("detach_other_clients"))?,
200 kill_other_clients: kill_other_clients
201 .ok_or_else(|| de::Error::missing_field("kill_other_clients"))?,
202 flags: flags.ok_or_else(|| de::Error::missing_field("flags"))?,
203 window_name: window_name.ok_or_else(|| de::Error::missing_field("window_name"))?,
204 print_session_info: print_session_info
205 .ok_or_else(|| de::Error::missing_field("print_session_info"))?,
206 print_format: print_format.ok_or_else(|| de::Error::missing_field("print_format"))?,
207 command: command.ok_or_else(|| de::Error::missing_field("command"))?,
208 process_command: process_command.unwrap_or_default(),
209 })
210 }
211}
212
213fn required_next<'de, A, T, V>(seq: &mut A, index: usize, visitor: &V) -> Result<T, A::Error>
214where
215 A: SeqAccess<'de>,
216 T: Deserialize<'de>,
217 V: Visitor<'de>,
218{
219 seq.next_element()?
220 .ok_or_else(|| de::Error::invalid_length(index, visitor))
221}
222
223fn compat_next_element<'de, A, T>(seq: &mut A) -> Result<T, A::Error>
224where
225 A: SeqAccess<'de>,
226 T: Deserialize<'de> + Default,
227{
228 match seq.next_element::<T>() {
229 Ok(Some(value)) => Ok(value),
230 Ok(None) => Ok(T::default()),
231 Err(error) if is_truncated_compat_sequence(&error) => Ok(T::default()),
232 Err(error) => Err(error),
233 }
234}
235
236fn is_truncated_compat_sequence(error: &impl std::fmt::Display) -> bool {
237 let message = error.to_string();
238 message.contains("UnexpectedEof") || message.contains("unexpected end of file")
239}
240
241#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
243pub struct HasSessionRequest {
244 pub target: SessionName,
246}
247
248#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
250pub struct KillSessionRequest {
251 pub target: SessionName,
253 #[serde(default)]
255 pub kill_all_except_target: bool,
256 #[serde(default)]
258 pub clear_alerts: bool,
259}
260
261#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
263pub struct CreateSessionLeaseRequest {
264 pub session_name: SessionName,
266 pub ttl_millis: u64,
268}
269
270#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
272pub struct RenewSessionLeaseRequest {
273 pub session_name: SessionName,
275 pub token: u64,
277 pub ttl_millis: u64,
279}
280
281#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
283pub struct ReleaseSessionLeaseRequest {
284 pub session_name: SessionName,
286 pub token: u64,
288}
289
290#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
292pub struct RenameSessionRequest {
293 pub target: SessionName,
295 pub new_name: SessionName,
297}
298
299#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
301pub struct ListSessionsRequest {
302 pub format: Option<String>,
304 #[serde(default)]
306 pub filter: Option<String>,
307 #[serde(default)]
309 pub sort_order: Option<String>,
310 #[serde(default)]
312 pub reversed: bool,
313}