Skip to main content

rmux_proto/request/
session.rs

1use serde::de::{self, MapAccess, SeqAccess, Visitor};
2use serde::{Deserialize, Deserializer, Serialize};
3
4use crate::{ProcessCommand, SessionName, TerminalSize};
5
6use super::compat::{compat_next_element, required_next};
7
8/// Request payload for `new-session`.
9#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
10pub struct NewSessionRequest {
11    /// The exact session name to create.
12    pub session_name: SessionName,
13    /// Whether the session should remain detached after creation.
14    pub detached: bool,
15    /// The initial pane geometry, when explicitly requested.
16    pub size: Option<TerminalSize>,
17    /// Optional per-spawn environment overrides in `NAME=VALUE` form.
18    #[serde(default)]
19    pub environment: Option<Vec<String>>,
20}
21
22/// Extended request payload for `new-session`.
23#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
24pub struct NewSessionExtRequest {
25    /// The optional exact session name to create.
26    pub session_name: Option<SessionName>,
27    /// Optional tmux format-expanded start directory for the new session.
28    #[serde(default)]
29    pub working_directory: Option<String>,
30    /// Whether the session should remain detached after creation.
31    pub detached: bool,
32    /// The initial pane geometry, when explicitly requested.
33    pub size: Option<TerminalSize>,
34    /// Optional per-spawn environment overrides in `NAME=VALUE` form.
35    #[serde(default)]
36    pub environment: Option<Vec<String>>,
37    /// The optional target session or group name for grouped-session creation.
38    #[serde(default)]
39    pub group_target: Option<SessionName>,
40    /// Whether an existing target session should be attached instead of erroring.
41    #[serde(default)]
42    pub attach_if_exists: bool,
43    /// Whether other attached clients should be detached before attaching.
44    #[serde(default)]
45    pub detach_other_clients: bool,
46    /// Whether other attached clients should be detached and terminated.
47    #[serde(default)]
48    pub kill_other_clients: bool,
49    /// Optional tmux client-flag names such as `read-only` or `active-pane`.
50    #[serde(default)]
51    pub flags: Option<Vec<String>>,
52    /// The optional initial active-window name for standalone session creation.
53    #[serde(default)]
54    pub window_name: Option<String>,
55    /// Whether the created session should print formatted session information.
56    #[serde(default)]
57    pub print_session_info: bool,
58    /// The optional format template used when printing session information.
59    #[serde(default)]
60    pub print_format: Option<String>,
61    /// Legacy optional shell command argv. A single argument is executed via
62    /// `$SHELL -c`.
63    #[serde(default)]
64    pub command: Option<Vec<String>>,
65    /// Explicit process launch mode for the initial pane.
66    #[serde(default)]
67    pub process_command: Option<ProcessCommand>,
68    /// Full invoking client environment in `NAME=VALUE` form.
69    #[serde(default)]
70    pub client_environment: Option<Vec<String>>,
71    /// Whether session creation should skip updating from the invoking client environment.
72    #[serde(default)]
73    pub skip_environment_update: bool,
74}
75
76impl<'de> Deserialize<'de> for NewSessionExtRequest {
77    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
78    where
79        D: Deserializer<'de>,
80    {
81        deserializer.deserialize_struct(
82            "NewSessionExtRequest",
83            &[
84                "session_name",
85                "working_directory",
86                "detached",
87                "size",
88                "environment",
89                "group_target",
90                "attach_if_exists",
91                "detach_other_clients",
92                "kill_other_clients",
93                "flags",
94                "window_name",
95                "print_session_info",
96                "print_format",
97                "command",
98                "process_command",
99                "client_environment",
100                "skip_environment_update",
101            ],
102            NewSessionExtRequestVisitor,
103        )
104    }
105}
106
107struct NewSessionExtRequestVisitor;
108
109impl<'de> Visitor<'de> for NewSessionExtRequestVisitor {
110    type Value = NewSessionExtRequest;
111
112    fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
113        formatter.write_str("a new-session extended request")
114    }
115
116    fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
117    where
118        A: SeqAccess<'de>,
119    {
120        let session_name = required_next(&mut seq, 0, &self)?;
121        let working_directory = required_next(&mut seq, 1, &self)?;
122        let detached = required_next(&mut seq, 2, &self)?;
123        let size = required_next(&mut seq, 3, &self)?;
124        let environment = required_next(&mut seq, 4, &self)?;
125        let group_target = required_next(&mut seq, 5, &self)?;
126        let attach_if_exists = required_next(&mut seq, 6, &self)?;
127        let detach_other_clients = required_next(&mut seq, 7, &self)?;
128        let kill_other_clients = required_next(&mut seq, 8, &self)?;
129        let flags = required_next(&mut seq, 9, &self)?;
130        let window_name = required_next(&mut seq, 10, &self)?;
131        let print_session_info = required_next(&mut seq, 11, &self)?;
132        let print_format = required_next(&mut seq, 12, &self)?;
133        let command = required_next(&mut seq, 13, &self)?;
134        let process_command = compat_next_element(&mut seq)?;
135        let client_environment = compat_next_element(&mut seq)?;
136        let skip_environment_update: bool = compat_next_element(&mut seq)?;
137
138        Ok(NewSessionExtRequest {
139            session_name,
140            working_directory,
141            detached,
142            size,
143            environment,
144            group_target,
145            attach_if_exists,
146            detach_other_clients,
147            kill_other_clients,
148            flags,
149            window_name,
150            print_session_info,
151            print_format,
152            command,
153            process_command,
154            client_environment,
155            skip_environment_update,
156        })
157    }
158
159    fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
160    where
161        A: MapAccess<'de>,
162    {
163        let mut session_name = None;
164        let mut working_directory = None;
165        let mut detached = None;
166        let mut size = None;
167        let mut environment = None;
168        let mut group_target = None;
169        let mut attach_if_exists = None;
170        let mut detach_other_clients = None;
171        let mut kill_other_clients = None;
172        let mut flags = None;
173        let mut window_name = None;
174        let mut print_session_info = None;
175        let mut print_format = None;
176        let mut command = None;
177        let mut process_command = None;
178        let mut client_environment = None;
179        let mut skip_environment_update = None;
180
181        while let Some(key) = map.next_key::<String>()? {
182            match key.as_str() {
183                "session_name" => session_name = Some(map.next_value()?),
184                "working_directory" => working_directory = Some(map.next_value()?),
185                "detached" => detached = Some(map.next_value()?),
186                "size" => size = Some(map.next_value()?),
187                "environment" => environment = Some(map.next_value()?),
188                "group_target" => group_target = Some(map.next_value()?),
189                "attach_if_exists" => attach_if_exists = Some(map.next_value()?),
190                "detach_other_clients" => detach_other_clients = Some(map.next_value()?),
191                "kill_other_clients" => kill_other_clients = Some(map.next_value()?),
192                "flags" => flags = Some(map.next_value()?),
193                "window_name" => window_name = Some(map.next_value()?),
194                "print_session_info" => print_session_info = Some(map.next_value()?),
195                "print_format" => print_format = Some(map.next_value()?),
196                "command" => command = Some(map.next_value()?),
197                "process_command" => process_command = Some(map.next_value()?),
198                "client_environment" => client_environment = Some(map.next_value()?),
199                "skip_environment_update" => skip_environment_update = Some(map.next_value()?),
200                _ => {
201                    let _: de::IgnoredAny = map.next_value()?;
202                }
203            }
204        }
205
206        Ok(NewSessionExtRequest {
207            session_name: session_name.unwrap_or_default(),
208            working_directory: working_directory.unwrap_or_default(),
209            detached: detached.ok_or_else(|| de::Error::missing_field("detached"))?,
210            size: size.unwrap_or_default(),
211            environment: environment.unwrap_or_default(),
212            group_target: group_target.unwrap_or_default(),
213            attach_if_exists: attach_if_exists.unwrap_or_default(),
214            detach_other_clients: detach_other_clients.unwrap_or_default(),
215            kill_other_clients: kill_other_clients.unwrap_or_default(),
216            flags: flags.unwrap_or_default(),
217            window_name: window_name.unwrap_or_default(),
218            print_session_info: print_session_info.unwrap_or_default(),
219            print_format: print_format.unwrap_or_default(),
220            command: command.unwrap_or_default(),
221            process_command: process_command.unwrap_or_default(),
222            client_environment: client_environment.unwrap_or_default(),
223            skip_environment_update: skip_environment_update.unwrap_or_default(),
224        })
225    }
226}
227
228/// Request payload for `has-session`.
229#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
230pub struct HasSessionRequest {
231    /// The exact target session name.
232    pub target: SessionName,
233}
234
235/// Request payload for `kill-session`.
236#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
237pub struct KillSessionRequest {
238    /// The exact target session name.
239    pub target: SessionName,
240    /// Whether every other session should be destroyed instead of the target session.
241    #[serde(default)]
242    pub kill_all_except_target: bool,
243    /// Whether the target session's window alert flags should be cleared instead of destroying it.
244    #[serde(default)]
245    pub clear_alerts: bool,
246    /// Whether every session in the target session's group should be destroyed.
247    #[serde(default)]
248    pub kill_group: bool,
249}
250
251/// Request payload for creating an app-owner lease for one session.
252#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
253pub struct CreateSessionLeaseRequest {
254    /// Session kept alive only while the owner renews this lease.
255    pub session_name: SessionName,
256    /// Requested lease time-to-live in milliseconds.
257    pub ttl_millis: u64,
258}
259
260/// Request payload for renewing an app-owner session lease.
261#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
262pub struct RenewSessionLeaseRequest {
263    /// Leased session name.
264    pub session_name: SessionName,
265    /// Server-issued lease token.
266    pub token: u64,
267    /// Requested renewed time-to-live in milliseconds.
268    pub ttl_millis: u64,
269}
270
271/// Request payload for releasing an app-owner session lease.
272#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
273pub struct ReleaseSessionLeaseRequest {
274    /// Leased session name.
275    pub session_name: SessionName,
276    /// Server-issued lease token.
277    pub token: u64,
278}
279
280/// Request payload for `rename-session`.
281#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
282pub struct RenameSessionRequest {
283    /// The exact existing session name.
284    pub target: SessionName,
285    /// The validated destination session name.
286    pub new_name: SessionName,
287}
288
289/// Request payload for `list-sessions`.
290#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
291pub struct ListSessionsRequest {
292    /// An optional server-side format template.
293    pub format: Option<String>,
294    /// An optional server-side filter expression.
295    #[serde(default)]
296    pub filter: Option<String>,
297    /// The optional tmux sort order name.
298    #[serde(default)]
299    pub sort_order: Option<String>,
300    /// Whether the selected sort order should be reversed.
301    #[serde(default)]
302    pub reversed: bool,
303}