Skip to main content

iterm2_client/
request.rs

1use crate::proto;
2use crate::proto::client_originated_message::Submessage;
3
4fn wrap(submessage: Submessage) -> proto::ClientOriginatedMessage {
5    proto::ClientOriginatedMessage {
6        id: None, // Connection::call() sets this
7        submessage: Some(submessage),
8    }
9}
10
11// --- Session operations ---
12
13pub fn list_sessions() -> proto::ClientOriginatedMessage {
14    wrap(Submessage::ListSessionsRequest(proto::ListSessionsRequest {}))
15}
16
17pub fn send_text(session_id: &str, text: &str) -> proto::ClientOriginatedMessage {
18    wrap(Submessage::SendTextRequest(proto::SendTextRequest {
19        session: Some(session_id.to_string()),
20        text: Some(text.to_string()),
21        suppress_broadcast: None,
22    }))
23}
24
25pub fn get_buffer(
26    session_id: &str,
27    line_range: Option<proto::LineRange>,
28) -> proto::ClientOriginatedMessage {
29    wrap(Submessage::GetBufferRequest(proto::GetBufferRequest {
30        session: Some(session_id.to_string()),
31        line_range,
32        include_styles: Some(false),
33    }))
34}
35
36pub fn get_buffer_trailing(session_id: &str, lines: i32) -> proto::ClientOriginatedMessage {
37    get_buffer(
38        session_id,
39        Some(proto::LineRange {
40            screen_contents_only: None,
41            trailing_lines: Some(lines),
42            windowed_coord_range: None,
43        }),
44    )
45}
46
47pub fn get_buffer_screen(session_id: &str) -> proto::ClientOriginatedMessage {
48    get_buffer(
49        session_id,
50        Some(proto::LineRange {
51            screen_contents_only: Some(true),
52            trailing_lines: None,
53            windowed_coord_range: None,
54        }),
55    )
56}
57
58pub fn get_prompt(session_id: &str) -> proto::ClientOriginatedMessage {
59    wrap(Submessage::GetPromptRequest(proto::GetPromptRequest {
60        session: Some(session_id.to_string()),
61        unique_prompt_id: None,
62    }))
63}
64
65pub fn get_prompt_by_id(session_id: &str, prompt_id: &str) -> proto::ClientOriginatedMessage {
66    wrap(Submessage::GetPromptRequest(proto::GetPromptRequest {
67        session: Some(session_id.to_string()),
68        unique_prompt_id: Some(prompt_id.to_string()),
69    }))
70}
71
72pub fn list_prompts(session_id: &str) -> proto::ClientOriginatedMessage {
73    wrap(Submessage::ListPromptsRequest(proto::ListPromptsRequest {
74        session: Some(session_id.to_string()),
75        first_unique_id: None,
76        last_unique_id: None,
77    }))
78}
79
80// --- Tab operations ---
81
82pub fn create_tab(
83    profile_name: Option<&str>,
84    window_id: Option<&str>,
85) -> proto::ClientOriginatedMessage {
86    wrap(Submessage::CreateTabRequest(proto::CreateTabRequest {
87        profile_name: profile_name.map(|s| s.to_string()),
88        window_id: window_id.map(|s| s.to_string()),
89        tab_index: None,
90        #[allow(deprecated)]
91        command: None,
92        custom_profile_properties: vec![],
93    }))
94}
95
96pub fn split_pane(
97    session_id: &str,
98    direction: proto::split_pane_request::SplitDirection,
99    before: bool,
100    profile_name: Option<&str>,
101) -> proto::ClientOriginatedMessage {
102    wrap(Submessage::SplitPaneRequest(proto::SplitPaneRequest {
103        session: Some(session_id.to_string()),
104        split_direction: Some(direction.into()),
105        before: Some(before),
106        profile_name: profile_name.map(|s| s.to_string()),
107        custom_profile_properties: vec![],
108    }))
109}
110
111// --- Profile operations ---
112
113pub fn get_profile_property(
114    session_id: &str,
115    keys: Vec<String>,
116) -> proto::ClientOriginatedMessage {
117    wrap(Submessage::GetProfilePropertyRequest(
118        proto::GetProfilePropertyRequest {
119            session: Some(session_id.to_string()),
120            keys,
121        },
122    ))
123}
124
125pub fn set_profile_property_session(
126    session_id: &str,
127    key: &str,
128    json_value: &str,
129) -> proto::ClientOriginatedMessage {
130    wrap(Submessage::SetProfilePropertyRequest(
131        proto::SetProfilePropertyRequest {
132            target: Some(proto::set_profile_property_request::Target::Session(
133                session_id.to_string(),
134            )),
135            key: Some(key.to_string()),
136            json_value: Some(json_value.to_string()),
137            assignments: vec![],
138        },
139    ))
140}
141
142pub fn list_profiles(
143    properties: Vec<String>,
144    guids: Vec<String>,
145) -> proto::ClientOriginatedMessage {
146    wrap(Submessage::ListProfilesRequest(proto::ListProfilesRequest {
147        properties,
148        guids,
149    }))
150}
151
152// --- Property operations ---
153
154pub fn get_property_window(window_id: &str, name: &str) -> proto::ClientOriginatedMessage {
155    wrap(Submessage::GetPropertyRequest(proto::GetPropertyRequest {
156        identifier: Some(proto::get_property_request::Identifier::WindowId(
157            window_id.to_string(),
158        )),
159        name: Some(name.to_string()),
160    }))
161}
162
163pub fn get_property_session(session_id: &str, name: &str) -> proto::ClientOriginatedMessage {
164    wrap(Submessage::GetPropertyRequest(proto::GetPropertyRequest {
165        identifier: Some(proto::get_property_request::Identifier::SessionId(
166            session_id.to_string(),
167        )),
168        name: Some(name.to_string()),
169    }))
170}
171
172pub fn set_property_window(
173    window_id: &str,
174    name: &str,
175    json_value: &str,
176) -> proto::ClientOriginatedMessage {
177    wrap(Submessage::SetPropertyRequest(proto::SetPropertyRequest {
178        identifier: Some(proto::set_property_request::Identifier::WindowId(
179            window_id.to_string(),
180        )),
181        name: Some(name.to_string()),
182        json_value: Some(json_value.to_string()),
183    }))
184}
185
186pub fn set_property_session(
187    session_id: &str,
188    name: &str,
189    json_value: &str,
190) -> proto::ClientOriginatedMessage {
191    wrap(Submessage::SetPropertyRequest(proto::SetPropertyRequest {
192        identifier: Some(proto::set_property_request::Identifier::SessionId(
193            session_id.to_string(),
194        )),
195        name: Some(name.to_string()),
196        json_value: Some(json_value.to_string()),
197    }))
198}
199
200// --- Variable operations ---
201
202pub fn get_variable_session(session_id: &str, names: Vec<String>) -> proto::ClientOriginatedMessage {
203    wrap(Submessage::VariableRequest(proto::VariableRequest {
204        scope: Some(proto::variable_request::Scope::SessionId(
205            session_id.to_string(),
206        )),
207        set: vec![],
208        get: names,
209    }))
210}
211
212pub fn set_variable_session(
213    session_id: &str,
214    sets: Vec<(String, String)>,
215) -> proto::ClientOriginatedMessage {
216    wrap(Submessage::VariableRequest(proto::VariableRequest {
217        scope: Some(proto::variable_request::Scope::SessionId(
218            session_id.to_string(),
219        )),
220        set: sets
221            .into_iter()
222            .map(|(name, value)| proto::variable_request::Set {
223                name: Some(name),
224                value: Some(value),
225            })
226            .collect(),
227        get: vec![],
228    }))
229}
230
231pub fn get_variable_app(names: Vec<String>) -> proto::ClientOriginatedMessage {
232    wrap(Submessage::VariableRequest(proto::VariableRequest {
233        scope: Some(proto::variable_request::Scope::App(true)),
234        set: vec![],
235        get: names,
236    }))
237}
238
239pub fn get_variable_tab(tab_id: &str, names: Vec<String>) -> proto::ClientOriginatedMessage {
240    wrap(Submessage::VariableRequest(proto::VariableRequest {
241        scope: Some(proto::variable_request::Scope::TabId(tab_id.to_string())),
242        set: vec![],
243        get: names,
244    }))
245}
246
247pub fn get_variable_window(window_id: &str, names: Vec<String>) -> proto::ClientOriginatedMessage {
248    wrap(Submessage::VariableRequest(proto::VariableRequest {
249        scope: Some(proto::variable_request::Scope::WindowId(window_id.to_string())),
250        set: vec![],
251        get: names,
252    }))
253}
254
255// --- Activate operations ---
256
257pub fn activate_session(session_id: &str) -> proto::ClientOriginatedMessage {
258    wrap(Submessage::ActivateRequest(proto::ActivateRequest {
259        identifier: Some(proto::activate_request::Identifier::SessionId(
260            session_id.to_string(),
261        )),
262        order_window_front: Some(true),
263        select_tab: Some(true),
264        select_session: Some(true),
265        activate_app: None,
266    }))
267}
268
269pub fn activate_tab(tab_id: &str) -> proto::ClientOriginatedMessage {
270    wrap(Submessage::ActivateRequest(proto::ActivateRequest {
271        identifier: Some(proto::activate_request::Identifier::TabId(
272            tab_id.to_string(),
273        )),
274        order_window_front: Some(true),
275        select_tab: Some(true),
276        select_session: None,
277        activate_app: None,
278    }))
279}
280
281pub fn activate_window(window_id: &str) -> proto::ClientOriginatedMessage {
282    wrap(Submessage::ActivateRequest(proto::ActivateRequest {
283        identifier: Some(proto::activate_request::Identifier::WindowId(
284            window_id.to_string(),
285        )),
286        order_window_front: Some(true),
287        select_tab: None,
288        select_session: None,
289        activate_app: None,
290    }))
291}
292
293pub fn activate_app(raise_all: bool, ignoring_other_apps: bool) -> proto::ClientOriginatedMessage {
294    wrap(Submessage::ActivateRequest(proto::ActivateRequest {
295        identifier: None,
296        order_window_front: None,
297        select_tab: None,
298        select_session: None,
299        activate_app: Some(proto::activate_request::App {
300            raise_all_windows: Some(raise_all),
301            ignoring_other_apps: Some(ignoring_other_apps),
302        }),
303    }))
304}
305
306// --- Transaction operations ---
307
308pub fn begin_transaction() -> proto::ClientOriginatedMessage {
309    wrap(Submessage::TransactionRequest(proto::TransactionRequest {
310        begin: Some(true),
311    }))
312}
313
314pub fn end_transaction() -> proto::ClientOriginatedMessage {
315    wrap(Submessage::TransactionRequest(proto::TransactionRequest {
316        begin: Some(false),
317    }))
318}
319
320// --- Notification subscription ---
321
322pub fn subscribe_notification(
323    notification_type: proto::NotificationType,
324    session_id: Option<&str>,
325) -> proto::ClientOriginatedMessage {
326    wrap(Submessage::NotificationRequest(proto::NotificationRequest {
327        session: session_id.map(|s| s.to_string()),
328        subscribe: Some(true),
329        notification_type: Some(notification_type.into()),
330        arguments: None,
331    }))
332}
333
334pub fn unsubscribe_notification(
335    notification_type: proto::NotificationType,
336    session_id: Option<&str>,
337) -> proto::ClientOriginatedMessage {
338    wrap(Submessage::NotificationRequest(proto::NotificationRequest {
339        session: session_id.map(|s| s.to_string()),
340        subscribe: Some(false),
341        notification_type: Some(notification_type.into()),
342        arguments: None,
343    }))
344}
345
346// --- Inject ---
347
348pub fn inject(session_ids: Vec<String>, data: Vec<u8>) -> proto::ClientOriginatedMessage {
349    wrap(Submessage::InjectRequest(proto::InjectRequest {
350        session_id: session_ids,
351        data: Some(data),
352    }))
353}
354
355// --- Close ---
356
357pub fn close_sessions(session_ids: Vec<String>, force: bool) -> proto::ClientOriginatedMessage {
358    wrap(Submessage::CloseRequest(proto::CloseRequest {
359        target: Some(proto::close_request::Target::Sessions(
360            proto::close_request::CloseSessions { session_ids },
361        )),
362        force: Some(force),
363    }))
364}
365
366pub fn close_tabs(tab_ids: Vec<String>, force: bool) -> proto::ClientOriginatedMessage {
367    wrap(Submessage::CloseRequest(proto::CloseRequest {
368        target: Some(proto::close_request::Target::Tabs(
369            proto::close_request::CloseTabs { tab_ids },
370        )),
371        force: Some(force),
372    }))
373}
374
375pub fn close_windows(window_ids: Vec<String>, force: bool) -> proto::ClientOriginatedMessage {
376    wrap(Submessage::CloseRequest(proto::CloseRequest {
377        target: Some(proto::close_request::Target::Windows(
378            proto::close_request::CloseWindows { window_ids },
379        )),
380        force: Some(force),
381    }))
382}
383
384// --- Focus ---
385
386pub fn focus() -> proto::ClientOriginatedMessage {
387    wrap(Submessage::FocusRequest(proto::FocusRequest {}))
388}
389
390// --- Saved arrangements ---
391
392pub fn restore_arrangement(name: &str, window_id: Option<&str>) -> proto::ClientOriginatedMessage {
393    wrap(Submessage::SavedArrangementRequest(
394        proto::SavedArrangementRequest {
395            name: Some(name.to_string()),
396            action: Some(proto::saved_arrangement_request::Action::Restore.into()),
397            window_id: window_id.map(|s| s.to_string()),
398        },
399    ))
400}
401
402pub fn save_arrangement(name: &str, window_id: Option<&str>) -> proto::ClientOriginatedMessage {
403    wrap(Submessage::SavedArrangementRequest(
404        proto::SavedArrangementRequest {
405            name: Some(name.to_string()),
406            action: Some(proto::saved_arrangement_request::Action::Save.into()),
407            window_id: window_id.map(|s| s.to_string()),
408        },
409    ))
410}
411
412pub fn list_arrangements() -> proto::ClientOriginatedMessage {
413    wrap(Submessage::SavedArrangementRequest(
414        proto::SavedArrangementRequest {
415            name: None,
416            action: Some(proto::saved_arrangement_request::Action::List.into()),
417            window_id: None,
418        },
419    ))
420}
421
422// --- Menu item ---
423
424pub fn invoke_menu_item(identifier: &str) -> proto::ClientOriginatedMessage {
425    wrap(Submessage::MenuItemRequest(proto::MenuItemRequest {
426        identifier: Some(identifier.to_string()),
427        query_only: Some(false),
428    }))
429}
430
431pub fn query_menu_item(identifier: &str) -> proto::ClientOriginatedMessage {
432    wrap(Submessage::MenuItemRequest(proto::MenuItemRequest {
433        identifier: Some(identifier.to_string()),
434        query_only: Some(true),
435    }))
436}
437
438// --- Restart session ---
439
440pub fn restart_session(session_id: &str, only_if_exited: bool) -> proto::ClientOriginatedMessage {
441    wrap(Submessage::RestartSessionRequest(
442        proto::RestartSessionRequest {
443            session_id: Some(session_id.to_string()),
444            only_if_exited: Some(only_if_exited),
445        },
446    ))
447}
448
449// --- Register tool ---
450
451pub fn register_tool(
452    name: &str,
453    identifier: &str,
454    url: &str,
455) -> proto::ClientOriginatedMessage {
456    wrap(Submessage::RegisterToolRequest(proto::RegisterToolRequest {
457        name: Some(name.to_string()),
458        identifier: Some(identifier.to_string()),
459        tool_type: Some(proto::register_tool_request::ToolType::WebViewTool.into()),
460        reveal_if_already_registered: Some(false),
461        url: Some(url.to_string()),
462    }))
463}
464
465// --- Set tab layout ---
466
467pub fn set_tab_layout(
468    tab_id: &str,
469    root: proto::SplitTreeNode,
470) -> proto::ClientOriginatedMessage {
471    wrap(Submessage::SetTabLayoutRequest(proto::SetTabLayoutRequest {
472        tab_id: Some(tab_id.to_string()),
473        root: Some(root),
474    }))
475}
476
477// --- Broadcast domains ---
478
479pub fn get_broadcast_domains() -> proto::ClientOriginatedMessage {
480    wrap(Submessage::GetBroadcastDomainsRequest(
481        proto::GetBroadcastDomainsRequest {},
482    ))
483}
484
485pub fn set_broadcast_domains(
486    domains: Vec<proto::BroadcastDomain>,
487) -> proto::ClientOriginatedMessage {
488    wrap(Submessage::SetBroadcastDomainsRequest(
489        proto::SetBroadcastDomainsRequest {
490            broadcast_domains: domains,
491        },
492    ))
493}
494
495// --- Tmux ---
496
497pub fn tmux_list_connections() -> proto::ClientOriginatedMessage {
498    wrap(Submessage::TmuxRequest(proto::TmuxRequest {
499        payload: Some(proto::tmux_request::Payload::ListConnections(
500            proto::tmux_request::ListConnections {},
501        )),
502    }))
503}
504
505pub fn tmux_send_command(
506    connection_id: &str,
507    command: &str,
508) -> proto::ClientOriginatedMessage {
509    wrap(Submessage::TmuxRequest(proto::TmuxRequest {
510        payload: Some(proto::tmux_request::Payload::SendCommand(
511            proto::tmux_request::SendCommand {
512                connection_id: Some(connection_id.to_string()),
513                command: Some(command.to_string()),
514            },
515        )),
516    }))
517}
518
519// --- Reorder tabs ---
520
521pub fn reorder_tabs(
522    assignments: Vec<proto::reorder_tabs_request::Assignment>,
523) -> proto::ClientOriginatedMessage {
524    wrap(Submessage::ReorderTabsRequest(proto::ReorderTabsRequest {
525        assignments,
526    }))
527}
528
529// --- Preferences ---
530
531pub fn get_preference(key: &str) -> proto::ClientOriginatedMessage {
532    wrap(Submessage::PreferencesRequest(proto::PreferencesRequest {
533        requests: vec![proto::preferences_request::Request {
534            request: Some(
535                proto::preferences_request::request::Request::GetPreferenceRequest(
536                    proto::preferences_request::request::GetPreference {
537                        key: Some(key.to_string()),
538                    },
539                ),
540            ),
541        }],
542    }))
543}
544
545// --- Color presets ---
546
547pub fn list_color_presets() -> proto::ClientOriginatedMessage {
548    wrap(Submessage::ColorPresetRequest(proto::ColorPresetRequest {
549        request: Some(proto::color_preset_request::Request::ListPresets(
550            proto::color_preset_request::ListPresets {},
551        )),
552    }))
553}
554
555pub fn get_color_preset(name: &str) -> proto::ClientOriginatedMessage {
556    wrap(Submessage::ColorPresetRequest(proto::ColorPresetRequest {
557        request: Some(proto::color_preset_request::Request::GetPreset(
558            proto::color_preset_request::GetPreset {
559                name: Some(name.to_string()),
560            },
561        )),
562    }))
563}
564
565// --- Selection ---
566
567pub fn get_selection(session_id: &str) -> proto::ClientOriginatedMessage {
568    wrap(Submessage::SelectionRequest(proto::SelectionRequest {
569        request: Some(proto::selection_request::Request::GetSelectionRequest(
570            proto::selection_request::GetSelectionRequest {
571                session_id: Some(session_id.to_string()),
572            },
573        )),
574    }))
575}
576
577pub fn set_selection(
578    session_id: &str,
579    selection: proto::Selection,
580) -> proto::ClientOriginatedMessage {
581    wrap(Submessage::SelectionRequest(proto::SelectionRequest {
582        request: Some(proto::selection_request::Request::SetSelectionRequest(
583            proto::selection_request::SetSelectionRequest {
584                session_id: Some(session_id.to_string()),
585                selection: Some(selection),
586            },
587        )),
588    }))
589}
590
591// --- Status bar component ---
592
593pub fn open_status_bar_popover(
594    identifier: &str,
595    session_id: &str,
596    html: &str,
597    width: i32,
598    height: i32,
599) -> proto::ClientOriginatedMessage {
600    wrap(Submessage::StatusBarComponentRequest(
601        proto::StatusBarComponentRequest {
602            request: Some(
603                proto::status_bar_component_request::Request::OpenPopover(
604                    proto::status_bar_component_request::OpenPopover {
605                        session_id: Some(session_id.to_string()),
606                        html: Some(html.to_string()),
607                        size: Some(proto::Size {
608                            width: Some(width),
609                            height: Some(height),
610                        }),
611                    },
612                ),
613            ),
614            identifier: Some(identifier.to_string()),
615        },
616    ))
617}
618
619// --- Invoke function ---
620
621pub fn invoke_function_app(invocation: &str) -> proto::ClientOriginatedMessage {
622    wrap(Submessage::InvokeFunctionRequest(
623        proto::InvokeFunctionRequest {
624            context: Some(proto::invoke_function_request::Context::App(
625                proto::invoke_function_request::App {},
626            )),
627            invocation: Some(invocation.to_string()),
628            timeout: None,
629        },
630    ))
631}
632
633pub fn invoke_function_session(
634    session_id: &str,
635    invocation: &str,
636) -> proto::ClientOriginatedMessage {
637    wrap(Submessage::InvokeFunctionRequest(
638        proto::InvokeFunctionRequest {
639            context: Some(proto::invoke_function_request::Context::Session(
640                proto::invoke_function_request::Session {
641                    session_id: Some(session_id.to_string()),
642                },
643            )),
644            invocation: Some(invocation.to_string()),
645            timeout: None,
646        },
647    ))
648}
649
650pub fn invoke_function_tab(tab_id: &str, invocation: &str) -> proto::ClientOriginatedMessage {
651    wrap(Submessage::InvokeFunctionRequest(
652        proto::InvokeFunctionRequest {
653            context: Some(proto::invoke_function_request::Context::Tab(
654                proto::invoke_function_request::Tab {
655                    tab_id: Some(tab_id.to_string()),
656                },
657            )),
658            invocation: Some(invocation.to_string()),
659            timeout: None,
660        },
661    ))
662}
663
664pub fn invoke_function_window(
665    window_id: &str,
666    invocation: &str,
667) -> proto::ClientOriginatedMessage {
668    wrap(Submessage::InvokeFunctionRequest(
669        proto::InvokeFunctionRequest {
670            context: Some(proto::invoke_function_request::Context::Window(
671                proto::invoke_function_request::Window {
672                    window_id: Some(window_id.to_string()),
673                },
674            )),
675            invocation: Some(invocation.to_string()),
676            timeout: None,
677        },
678    ))
679}
680
681// --- Server-originated RPC result ---
682
683pub fn rpc_result_value(request_id: &str, json_value: &str) -> proto::ClientOriginatedMessage {
684    wrap(Submessage::ServerOriginatedRpcResultRequest(
685        proto::ServerOriginatedRpcResultRequest {
686            request_id: Some(request_id.to_string()),
687            result: Some(
688                proto::server_originated_rpc_result_request::Result::JsonValue(
689                    json_value.to_string(),
690                ),
691            ),
692        },
693    ))
694}
695
696pub fn rpc_result_exception(
697    request_id: &str,
698    json_exception: &str,
699) -> proto::ClientOriginatedMessage {
700    wrap(Submessage::ServerOriginatedRpcResultRequest(
701        proto::ServerOriginatedRpcResultRequest {
702            request_id: Some(request_id.to_string()),
703            result: Some(
704                proto::server_originated_rpc_result_request::Result::JsonException(
705                    json_exception.to_string(),
706                ),
707            ),
708        },
709    ))
710}
711
712#[cfg(test)]
713mod tests {
714    use super::*;
715    use crate::proto::client_originated_message::Submessage;
716
717    #[test]
718    fn list_sessions_request() {
719        let msg = list_sessions();
720        assert!(msg.id.is_none());
721        assert!(matches!(
722            msg.submessage,
723            Some(Submessage::ListSessionsRequest(_))
724        ));
725    }
726
727    #[test]
728    fn send_text_request() {
729        let msg = send_text("session-123", "hello\n");
730        match msg.submessage {
731            Some(Submessage::SendTextRequest(req)) => {
732                assert_eq!(req.session.as_deref(), Some("session-123"));
733                assert_eq!(req.text.as_deref(), Some("hello\n"));
734            }
735            _ => panic!("wrong submessage"),
736        }
737    }
738
739    #[test]
740    fn get_buffer_trailing_request() {
741        let msg = get_buffer_trailing("s1", 50);
742        match msg.submessage {
743            Some(Submessage::GetBufferRequest(req)) => {
744                assert_eq!(req.session.as_deref(), Some("s1"));
745                assert_eq!(req.line_range.unwrap().trailing_lines, Some(50));
746            }
747            _ => panic!("wrong submessage"),
748        }
749    }
750
751    #[test]
752    fn get_buffer_screen_request() {
753        let msg = get_buffer_screen("s1");
754        match msg.submessage {
755            Some(Submessage::GetBufferRequest(req)) => {
756                assert_eq!(
757                    req.line_range.unwrap().screen_contents_only,
758                    Some(true)
759                );
760            }
761            _ => panic!("wrong submessage"),
762        }
763    }
764
765    #[test]
766    fn create_tab_request() {
767        let msg = create_tab(Some("Default"), Some("w1"));
768        match msg.submessage {
769            Some(Submessage::CreateTabRequest(req)) => {
770                assert_eq!(req.profile_name.as_deref(), Some("Default"));
771                assert_eq!(req.window_id.as_deref(), Some("w1"));
772            }
773            _ => panic!("wrong submessage"),
774        }
775    }
776
777    #[test]
778    fn split_pane_request() {
779        let msg = split_pane(
780            "s1",
781            proto::split_pane_request::SplitDirection::Vertical,
782            false,
783            None,
784        );
785        match msg.submessage {
786            Some(Submessage::SplitPaneRequest(req)) => {
787                assert_eq!(req.session.as_deref(), Some("s1"));
788                assert_eq!(req.split_direction, Some(0)); // VERTICAL = 0
789                assert_eq!(req.before, Some(false));
790            }
791            _ => panic!("wrong submessage"),
792        }
793    }
794
795    #[test]
796    fn get_profile_property_request() {
797        let msg = get_profile_property("s1", vec!["Name".to_string()]);
798        match msg.submessage {
799            Some(Submessage::GetProfilePropertyRequest(req)) => {
800                assert_eq!(req.session.as_deref(), Some("s1"));
801                assert_eq!(req.keys, vec!["Name".to_string()]);
802            }
803            _ => panic!("wrong submessage"),
804        }
805    }
806
807    #[test]
808    fn set_profile_property_request() {
809        let msg = set_profile_property_session("s1", "Badge Text", r#""hello""#);
810        match msg.submessage {
811            Some(Submessage::SetProfilePropertyRequest(req)) => {
812                assert_eq!(req.key.as_deref(), Some("Badge Text"));
813                assert_eq!(req.json_value.as_deref(), Some(r#""hello""#));
814            }
815            _ => panic!("wrong submessage"),
816        }
817    }
818
819    #[test]
820    fn variable_get_session_request() {
821        let msg = get_variable_session("s1", vec!["user.foo".to_string()]);
822        match msg.submessage {
823            Some(Submessage::VariableRequest(req)) => {
824                assert_eq!(req.get, vec!["user.foo".to_string()]);
825                assert!(req.set.is_empty());
826            }
827            _ => panic!("wrong submessage"),
828        }
829    }
830
831    #[test]
832    fn variable_set_session_request() {
833        let msg = set_variable_session(
834            "s1",
835            vec![("user.foo".to_string(), r#""bar""#.to_string())],
836        );
837        match msg.submessage {
838            Some(Submessage::VariableRequest(req)) => {
839                assert_eq!(req.set.len(), 1);
840                assert_eq!(req.set[0].name.as_deref(), Some("user.foo"));
841                assert_eq!(req.set[0].value.as_deref(), Some(r#""bar""#));
842            }
843            _ => panic!("wrong submessage"),
844        }
845    }
846
847    #[test]
848    fn transaction_begin_end() {
849        let begin = begin_transaction();
850        match begin.submessage {
851            Some(Submessage::TransactionRequest(req)) => {
852                assert_eq!(req.begin, Some(true));
853            }
854            _ => panic!("wrong submessage"),
855        }
856
857        let end = end_transaction();
858        match end.submessage {
859            Some(Submessage::TransactionRequest(req)) => {
860                assert_eq!(req.begin, Some(false));
861            }
862            _ => panic!("wrong submessage"),
863        }
864    }
865
866    #[test]
867    fn subscribe_unsubscribe_notification() {
868        let sub = subscribe_notification(
869            proto::NotificationType::NotifyOnNewSession,
870            None,
871        );
872        match sub.submessage {
873            Some(Submessage::NotificationRequest(req)) => {
874                assert_eq!(req.subscribe, Some(true));
875                assert_eq!(
876                    req.notification_type,
877                    Some(proto::NotificationType::NotifyOnNewSession as i32)
878                );
879            }
880            _ => panic!("wrong submessage"),
881        }
882
883        let unsub = unsubscribe_notification(
884            proto::NotificationType::NotifyOnNewSession,
885            None,
886        );
887        match unsub.submessage {
888            Some(Submessage::NotificationRequest(req)) => {
889                assert_eq!(req.subscribe, Some(false));
890            }
891            _ => panic!("wrong submessage"),
892        }
893    }
894
895    #[test]
896    fn close_sessions_request() {
897        let msg = close_sessions(vec!["s1".to_string(), "s2".to_string()], true);
898        match msg.submessage {
899            Some(Submessage::CloseRequest(req)) => {
900                assert_eq!(req.force, Some(true));
901                match req.target {
902                    Some(proto::close_request::Target::Sessions(s)) => {
903                        assert_eq!(s.session_ids, vec!["s1", "s2"]);
904                    }
905                    _ => panic!("wrong target"),
906                }
907            }
908            _ => panic!("wrong submessage"),
909        }
910    }
911
912    #[test]
913    fn focus_request() {
914        let msg = focus();
915        assert!(matches!(
916            msg.submessage,
917            Some(Submessage::FocusRequest(_))
918        ));
919    }
920
921    #[test]
922    fn activate_session_request() {
923        let msg = activate_session("s1");
924        match msg.submessage {
925            Some(Submessage::ActivateRequest(req)) => {
926                assert!(matches!(
927                    req.identifier,
928                    Some(proto::activate_request::Identifier::SessionId(_))
929                ));
930                assert_eq!(req.order_window_front, Some(true));
931                assert_eq!(req.select_tab, Some(true));
932                assert_eq!(req.select_session, Some(true));
933            }
934            _ => panic!("wrong submessage"),
935        }
936    }
937
938    #[test]
939    fn inject_request() {
940        let msg = inject(vec!["s1".to_string()], b"hello".to_vec());
941        match msg.submessage {
942            Some(Submessage::InjectRequest(req)) => {
943                assert_eq!(req.session_id, vec!["s1"]);
944                assert_eq!(req.data.as_deref(), Some(b"hello".as_slice()));
945            }
946            _ => panic!("wrong submessage"),
947        }
948    }
949
950    #[test]
951    fn menu_item_invoke_and_query() {
952        let msg = invoke_menu_item("menu.item.1");
953        match msg.submessage {
954            Some(Submessage::MenuItemRequest(req)) => {
955                assert_eq!(req.identifier.as_deref(), Some("menu.item.1"));
956                assert_eq!(req.query_only, Some(false));
957            }
958            _ => panic!("wrong submessage"),
959        }
960
961        let msg = query_menu_item("menu.item.1");
962        match msg.submessage {
963            Some(Submessage::MenuItemRequest(req)) => {
964                assert_eq!(req.query_only, Some(true));
965            }
966            _ => panic!("wrong submessage"),
967        }
968    }
969
970    #[test]
971    fn restart_session_request() {
972        let msg = restart_session("s1", true);
973        match msg.submessage {
974            Some(Submessage::RestartSessionRequest(req)) => {
975                assert_eq!(req.session_id.as_deref(), Some("s1"));
976                assert_eq!(req.only_if_exited, Some(true));
977            }
978            _ => panic!("wrong submessage"),
979        }
980    }
981
982    #[test]
983    fn saved_arrangement_list() {
984        let msg = list_arrangements();
985        match msg.submessage {
986            Some(Submessage::SavedArrangementRequest(req)) => {
987                assert_eq!(
988                    req.action,
989                    Some(proto::saved_arrangement_request::Action::List as i32)
990                );
991            }
992            _ => panic!("wrong submessage"),
993        }
994    }
995
996    #[test]
997    fn broadcast_domains_request() {
998        let msg = get_broadcast_domains();
999        assert!(matches!(
1000            msg.submessage,
1001            Some(Submessage::GetBroadcastDomainsRequest(_))
1002        ));
1003    }
1004
1005    #[test]
1006    fn tmux_list_request() {
1007        let msg = tmux_list_connections();
1008        match msg.submessage {
1009            Some(Submessage::TmuxRequest(req)) => {
1010                assert!(matches!(
1011                    req.payload,
1012                    Some(proto::tmux_request::Payload::ListConnections(_))
1013                ));
1014            }
1015            _ => panic!("wrong submessage"),
1016        }
1017    }
1018
1019    #[test]
1020    fn list_color_presets_request() {
1021        let msg = list_color_presets();
1022        match msg.submessage {
1023            Some(Submessage::ColorPresetRequest(req)) => {
1024                assert!(matches!(
1025                    req.request,
1026                    Some(proto::color_preset_request::Request::ListPresets(_))
1027                ));
1028            }
1029            _ => panic!("wrong submessage"),
1030        }
1031    }
1032
1033    #[test]
1034    fn get_selection_request() {
1035        let msg = get_selection("s1");
1036        match msg.submessage {
1037            Some(Submessage::SelectionRequest(req)) => {
1038                assert!(matches!(
1039                    req.request,
1040                    Some(proto::selection_request::Request::GetSelectionRequest(_))
1041                ));
1042            }
1043            _ => panic!("wrong submessage"),
1044        }
1045    }
1046
1047    #[test]
1048    fn invoke_function_app_request() {
1049        let msg = invoke_function_app("test()");
1050        match msg.submessage {
1051            Some(Submessage::InvokeFunctionRequest(req)) => {
1052                assert_eq!(req.invocation.as_deref(), Some("test()"));
1053                assert!(matches!(
1054                    req.context,
1055                    Some(proto::invoke_function_request::Context::App(_))
1056                ));
1057            }
1058            _ => panic!("wrong submessage"),
1059        }
1060    }
1061
1062    #[test]
1063    fn rpc_result_value_request() {
1064        let msg = rpc_result_value("req-1", r#""ok""#);
1065        match msg.submessage {
1066            Some(Submessage::ServerOriginatedRpcResultRequest(req)) => {
1067                assert_eq!(req.request_id.as_deref(), Some("req-1"));
1068            }
1069            _ => panic!("wrong submessage"),
1070        }
1071    }
1072
1073    #[test]
1074    fn list_profiles_request() {
1075        let msg = list_profiles(vec![], vec![]);
1076        assert!(matches!(
1077            msg.submessage,
1078            Some(Submessage::ListProfilesRequest(_))
1079        ));
1080    }
1081
1082    #[test]
1083    fn get_preference_request() {
1084        let msg = get_preference("SomeKey");
1085        assert!(matches!(
1086            msg.submessage,
1087            Some(Submessage::PreferencesRequest(_))
1088        ));
1089    }
1090
1091    #[test]
1092    fn register_tool_request() {
1093        let msg = register_tool("My Tool", "com.example.tool", "http://localhost:8080");
1094        match msg.submessage {
1095            Some(Submessage::RegisterToolRequest(req)) => {
1096                assert_eq!(req.name.as_deref(), Some("My Tool"));
1097                assert_eq!(req.identifier.as_deref(), Some("com.example.tool"));
1098            }
1099            _ => panic!("wrong submessage"),
1100        }
1101    }
1102
1103    #[test]
1104    fn get_property_window_request() {
1105        let msg = get_property_window("w1", "frame");
1106        match msg.submessage {
1107            Some(Submessage::GetPropertyRequest(req)) => {
1108                assert_eq!(req.name.as_deref(), Some("frame"));
1109                assert!(matches!(
1110                    req.identifier,
1111                    Some(proto::get_property_request::Identifier::WindowId(_))
1112                ));
1113            }
1114            _ => panic!("wrong submessage"),
1115        }
1116    }
1117
1118    #[test]
1119    fn set_property_window_request() {
1120        let msg = set_property_window("w1", "fullscreen", "true");
1121        match msg.submessage {
1122            Some(Submessage::SetPropertyRequest(req)) => {
1123                assert_eq!(req.name.as_deref(), Some("fullscreen"));
1124                assert_eq!(req.json_value.as_deref(), Some("true"));
1125            }
1126            _ => panic!("wrong submessage"),
1127        }
1128    }
1129}