1use crate::command::CommandBuilder;
2use crate::error::CommandError;
3use crate::protocol::KittyMessage;
4use serde::Deserialize;
5use serde_json::Map;
6
7#[derive(Debug, Deserialize)]
8pub struct ProcessInfo {
9 pub pid: Option<u64>,
10 #[serde(default)]
11 pub cmdline: Vec<String>,
12 pub cwd: Option<String>,
13}
14
15pub struct RunCommand {
16 data: Option<String>,
17 cmdline: Option<String>,
18 env: Option<Map<String, serde_json::Value>>,
19 allow_remote_control: bool,
20 remote_control_password: Option<String>,
21}
22
23impl RunCommand {
24 pub fn new() -> Self {
25 Self {
26 data: None,
27 cmdline: None,
28 env: None,
29 allow_remote_control: false,
30 remote_control_password: None,
31 }
32 }
33
34 pub fn data(mut self, value: impl Into<String>) -> Self {
35 self.data = Some(value.into());
36 self
37 }
38
39 pub fn cmdline(mut self, value: impl Into<String>) -> Self {
40 self.cmdline = Some(value.into());
41 self
42 }
43
44 pub fn env(mut self, value: Map<String, serde_json::Value>) -> Self {
45 self.env = Some(value);
46 self
47 }
48
49 pub fn allow_remote_control(mut self, value: bool) -> Self {
50 self.allow_remote_control = value;
51 self
52 }
53
54 pub fn remote_control_password(mut self, value: impl Into<String>) -> Self {
55 self.remote_control_password = Some(value.into());
56 self
57 }
58
59 pub fn build(self) -> Result<KittyMessage, CommandError> {
60 let mut payload = Map::new();
61
62 if let Some(data) = self.data {
63 payload.insert("data".to_string(), serde_json::Value::String(data));
64 }
65
66 if let Some(cmdline) = self.cmdline {
67 payload.insert("cmdline".to_string(), serde_json::Value::String(cmdline));
68 }
69
70 if let Some(env) = self.env {
71 payload.insert("env".to_string(), serde_json::Value::Object(env));
72 }
73
74 if self.allow_remote_control {
75 payload.insert(
76 "allow_remote_control".to_string(),
77 serde_json::Value::Bool(true),
78 );
79 }
80
81 if let Some(remote_control_password) = self.remote_control_password {
82 payload.insert(
83 "remote_control_password".to_string(),
84 serde_json::Value::String(remote_control_password),
85 );
86 }
87
88 Ok(CommandBuilder::new("run")
89 .payload(serde_json::Value::Object(payload))
90 .build())
91 }
92}
93
94pub struct KittenCommand {
95 args: Option<String>,
96 match_spec: Option<String>,
97}
98
99impl KittenCommand {
100 pub fn new() -> Self {
101 Self {
102 args: None,
103 match_spec: None,
104 }
105 }
106
107 pub fn args(mut self, value: impl Into<String>) -> Self {
108 self.args = Some(value.into());
109 self
110 }
111
112 pub fn match_spec(mut self, spec: impl Into<String>) -> Self {
113 self.match_spec = Some(spec.into());
114 self
115 }
116
117 pub fn build(self) -> Result<KittyMessage, CommandError> {
118 let mut payload = Map::new();
119
120 if let Some(args) = self.args {
121 payload.insert("args".to_string(), serde_json::Value::String(args));
122 }
123
124 if let Some(match_spec) = self.match_spec {
125 payload.insert("match".to_string(), serde_json::Value::String(match_spec));
126 }
127
128 Ok(CommandBuilder::new("kitten")
129 .payload(serde_json::Value::Object(payload))
130 .build())
131 }
132}
133
134pub struct LaunchCommand {
135 args: Option<String>,
136 window_title: Option<String>,
137 cwd: Option<String>,
138 env: Option<Map<String, serde_json::Value>>,
139 var: Option<Map<String, serde_json::Value>>,
140 tab_title: Option<String>,
141 window_type: Option<String>,
142 keep_focus: bool,
143 copy_colors: bool,
144 copy_cmdline: bool,
145 copy_env: bool,
146 hold: bool,
147 location: Option<String>,
148 allow_remote_control: bool,
149 remote_control_password: Option<String>,
150 stdin_source: Option<String>,
151 stdin_add_formatting: bool,
152 stdin_add_line_wrap_markers: bool,
153 spacing: Option<String>,
154 marker: Option<String>,
155 logo: Option<String>,
156 logo_position: Option<String>,
157 logo_alpha: Option<f32>,
158 self_window: bool,
159 os_window_title: Option<String>,
160 os_window_name: Option<String>,
161 os_window_class: Option<String>,
162 os_window_state: Option<String>,
163 color: Option<String>,
164 watcher: Option<String>,
165 bias: Option<i32>,
166}
167
168impl LaunchCommand {
169 pub fn new() -> Self {
170 Self {
171 args: None,
172 window_title: None,
173 cwd: None,
174 env: None,
175 var: None,
176 tab_title: None,
177 window_type: None,
178 keep_focus: false,
179 copy_colors: false,
180 copy_cmdline: false,
181 copy_env: false,
182 hold: false,
183 location: None,
184 allow_remote_control: false,
185 remote_control_password: None,
186 stdin_source: None,
187 stdin_add_formatting: false,
188 stdin_add_line_wrap_markers: false,
189 spacing: None,
190 marker: None,
191 logo: None,
192 logo_position: None,
193 logo_alpha: None,
194 self_window: false,
195 os_window_title: None,
196 os_window_name: None,
197 os_window_class: None,
198 os_window_state: None,
199 color: None,
200 watcher: None,
201 bias: None,
202 }
203 }
204
205 pub fn args(mut self, value: impl Into<String>) -> Self {
206 self.args = Some(value.into());
207 self
208 }
209
210 pub fn window_title(mut self, value: impl Into<String>) -> Self {
211 self.window_title = Some(value.into());
212 self
213 }
214
215 pub fn cwd(mut self, value: impl Into<String>) -> Self {
216 self.cwd = Some(value.into());
217 self
218 }
219
220 pub fn env(mut self, value: Map<String, serde_json::Value>) -> Self {
221 self.env = Some(value);
222 self
223 }
224
225 pub fn var(mut self, value: Map<String, serde_json::Value>) -> Self {
226 self.var = Some(value);
227 self
228 }
229
230 pub fn tab_title(mut self, value: impl Into<String>) -> Self {
231 self.tab_title = Some(value.into());
232 self
233 }
234
235 pub fn window_type(mut self, value: impl Into<String>) -> Self {
236 self.window_type = Some(value.into());
237 self
238 }
239
240 pub fn keep_focus(mut self, value: bool) -> Self {
241 self.keep_focus = value;
242 self
243 }
244
245 pub fn copy_colors(mut self, value: bool) -> Self {
246 self.copy_colors = value;
247 self
248 }
249
250 pub fn copy_cmdline(mut self, value: bool) -> Self {
251 self.copy_cmdline = value;
252 self
253 }
254
255 pub fn copy_env(mut self, value: bool) -> Self {
256 self.copy_env = value;
257 self
258 }
259
260 pub fn hold(mut self, value: bool) -> Self {
261 self.hold = value;
262 self
263 }
264
265 pub fn location(mut self, value: impl Into<String>) -> Self {
266 self.location = Some(value.into());
267 self
268 }
269
270 pub fn allow_remote_control(mut self, value: bool) -> Self {
271 self.allow_remote_control = value;
272 self
273 }
274
275 pub fn remote_control_password(mut self, value: impl Into<String>) -> Self {
276 self.remote_control_password = Some(value.into());
277 self
278 }
279
280 pub fn stdin_source(mut self, value: impl Into<String>) -> Self {
281 self.stdin_source = Some(value.into());
282 self
283 }
284
285 pub fn stdin_add_formatting(mut self, value: bool) -> Self {
286 self.stdin_add_formatting = value;
287 self
288 }
289
290 pub fn stdin_add_line_wrap_markers(mut self, value: bool) -> Self {
291 self.stdin_add_line_wrap_markers = value;
292 self
293 }
294
295 pub fn spacing(mut self, value: impl Into<String>) -> Self {
296 self.spacing = Some(value.into());
297 self
298 }
299
300 pub fn marker(mut self, value: impl Into<String>) -> Self {
301 self.marker = Some(value.into());
302 self
303 }
304
305 pub fn logo(mut self, value: impl Into<String>) -> Self {
306 self.logo = Some(value.into());
307 self
308 }
309
310 pub fn logo_position(mut self, value: impl Into<String>) -> Self {
311 self.logo_position = Some(value.into());
312 self
313 }
314
315 pub fn logo_alpha(mut self, value: f32) -> Self {
316 self.logo_alpha = Some(value);
317 self
318 }
319
320 pub fn self_window(mut self, value: bool) -> Self {
321 self.self_window = value;
322 self
323 }
324
325 pub fn os_window_title(mut self, value: impl Into<String>) -> Self {
326 self.os_window_title = Some(value.into());
327 self
328 }
329
330 pub fn os_window_name(mut self, value: impl Into<String>) -> Self {
331 self.os_window_name = Some(value.into());
332 self
333 }
334
335 pub fn os_window_class(mut self, value: impl Into<String>) -> Self {
336 self.os_window_class = Some(value.into());
337 self
338 }
339
340 pub fn os_window_state(mut self, value: impl Into<String>) -> Self {
341 self.os_window_state = Some(value.into());
342 self
343 }
344
345 pub fn color(mut self, value: impl Into<String>) -> Self {
346 self.color = Some(value.into());
347 self
348 }
349
350 pub fn watcher(mut self, value: impl Into<String>) -> Self {
351 self.watcher = Some(value.into());
352 self
353 }
354
355 pub fn bias(mut self, value: i32) -> Self {
356 self.bias = Some(value);
357 self
358 }
359
360 pub fn build(self) -> Result<KittyMessage, CommandError> {
361 let mut payload = Map::new();
362
363 if let Some(args) = self.args {
364 payload.insert("args".to_string(), serde_json::Value::String(args));
365 }
366
367 if let Some(window_title) = self.window_title {
368 payload.insert(
369 "window_title".to_string(),
370 serde_json::Value::String(window_title),
371 );
372 }
373
374 if let Some(cwd) = self.cwd {
375 payload.insert("cwd".to_string(), serde_json::Value::String(cwd));
376 }
377
378 if let Some(env) = self.env {
379 payload.insert("env".to_string(), serde_json::Value::Object(env));
380 }
381
382 if let Some(var) = self.var {
383 payload.insert("var".to_string(), serde_json::Value::Object(var));
384 }
385
386 if let Some(tab_title) = self.tab_title {
387 payload.insert(
388 "tab_title".to_string(),
389 serde_json::Value::String(tab_title),
390 );
391 }
392
393 if let Some(window_type) = self.window_type {
394 payload.insert(
395 "window_type".to_string(),
396 serde_json::Value::String(window_type),
397 );
398 }
399
400 if self.keep_focus {
401 payload.insert("keep_focus".to_string(), serde_json::Value::Bool(true));
402 }
403
404 if self.copy_colors {
405 payload.insert("copy_colors".to_string(), serde_json::Value::Bool(true));
406 }
407
408 if self.copy_cmdline {
409 payload.insert("copy_cmdline".to_string(), serde_json::Value::Bool(true));
410 }
411
412 if self.copy_env {
413 payload.insert("copy_env".to_string(), serde_json::Value::Bool(true));
414 }
415
416 if self.hold {
417 payload.insert("hold".to_string(), serde_json::Value::Bool(true));
418 }
419
420 if let Some(location) = self.location {
421 payload.insert("location".to_string(), serde_json::Value::String(location));
422 }
423
424 if self.allow_remote_control {
425 payload.insert(
426 "allow_remote_control".to_string(),
427 serde_json::Value::Bool(true),
428 );
429 }
430
431 if let Some(remote_control_password) = self.remote_control_password {
432 payload.insert(
433 "remote_control_password".to_string(),
434 serde_json::Value::String(remote_control_password),
435 );
436 }
437
438 if let Some(stdin_source) = self.stdin_source {
439 payload.insert(
440 "stdin_source".to_string(),
441 serde_json::Value::String(stdin_source),
442 );
443 }
444
445 if self.stdin_add_formatting {
446 payload.insert(
447 "stdin_add_formatting".to_string(),
448 serde_json::Value::Bool(true),
449 );
450 }
451
452 if self.stdin_add_line_wrap_markers {
453 payload.insert(
454 "stdin_add_line_wrap_markers".to_string(),
455 serde_json::Value::Bool(true),
456 );
457 }
458
459 if let Some(spacing) = self.spacing {
460 payload.insert("spacing".to_string(), serde_json::Value::String(spacing));
461 }
462
463 if let Some(marker) = self.marker {
464 payload.insert("marker".to_string(), serde_json::Value::String(marker));
465 }
466
467 if let Some(logo) = self.logo {
468 payload.insert("logo".to_string(), serde_json::Value::String(logo));
469 }
470
471 if let Some(logo_position) = self.logo_position {
472 payload.insert(
473 "logo_position".to_string(),
474 serde_json::Value::String(logo_position),
475 );
476 }
477
478 if let Some(logo_alpha) = self.logo_alpha {
479 payload.insert("logo_alpha".to_string(), serde_json::json!(logo_alpha));
480 }
481
482 if self.self_window {
483 payload.insert("self".to_string(), serde_json::Value::Bool(true));
484 }
485
486 if let Some(os_window_title) = self.os_window_title {
487 payload.insert(
488 "os_window_title".to_string(),
489 serde_json::Value::String(os_window_title),
490 );
491 }
492
493 if let Some(os_window_name) = self.os_window_name {
494 payload.insert(
495 "os_window_name".to_string(),
496 serde_json::Value::String(os_window_name),
497 );
498 }
499
500 if let Some(os_window_class) = self.os_window_class {
501 payload.insert(
502 "os_window_class".to_string(),
503 serde_json::Value::String(os_window_class),
504 );
505 }
506
507 if let Some(os_window_state) = self.os_window_state {
508 payload.insert(
509 "os_window_state".to_string(),
510 serde_json::Value::String(os_window_state),
511 );
512 }
513
514 if let Some(color) = self.color {
515 payload.insert("color".to_string(), serde_json::Value::String(color));
516 }
517
518 if let Some(watcher) = self.watcher {
519 payload.insert("watcher".to_string(), serde_json::Value::String(watcher));
520 }
521
522 if let Some(bias) = self.bias {
523 payload.insert("bias".to_string(), serde_json::json!(bias));
524 }
525
526 Ok(CommandBuilder::new("launch")
527 .payload(serde_json::Value::Object(payload))
528 .build())
529 }
530}
531
532pub struct EnvCommand {
533 env: Map<String, serde_json::Value>,
534}
535
536impl EnvCommand {
537 pub fn new(env: Map<String, serde_json::Value>) -> Self {
538 Self { env }
539 }
540
541 pub fn build(self) -> Result<KittyMessage, CommandError> {
542 let mut payload = Map::new();
543
544 if self.env.is_empty() {
545 return Err(CommandError::MissingParameter(
546 "env".to_string(),
547 "env".to_string(),
548 ));
549 }
550
551 payload.insert("env".to_string(), serde_json::Value::Object(self.env));
552
553 Ok(CommandBuilder::new("env")
554 .payload(serde_json::Value::Object(payload))
555 .build())
556 }
557}
558
559pub struct SetUserVarsCommand {
560 var: Vec<String>,
561 match_spec: Option<String>,
562}
563
564impl SetUserVarsCommand {
565 pub fn new(var: Vec<String>) -> Self {
566 Self {
567 var,
568 match_spec: None,
569 }
570 }
571
572 pub fn match_spec(mut self, spec: impl Into<String>) -> Self {
573 self.match_spec = Some(spec.into());
574 self
575 }
576
577 pub fn build(self) -> Result<KittyMessage, CommandError> {
578 let mut payload = Map::new();
579
580 if self.var.is_empty() {
581 return Err(CommandError::MissingParameter(
582 "var".to_string(),
583 "set-user-vars".to_string(),
584 ));
585 }
586
587 payload.insert("var".to_string(), serde_json::json!(self.var));
588
589 if let Some(match_spec) = self.match_spec {
590 payload.insert("match".to_string(), serde_json::Value::String(match_spec));
591 }
592
593 Ok(CommandBuilder::new("set-user-vars")
594 .payload(serde_json::Value::Object(payload))
595 .build())
596 }
597}
598
599pub struct LoadConfigCommand {
600 paths: Vec<String>,
601 override_config: bool,
602 ignore_overrides: bool,
603}
604
605impl LoadConfigCommand {
606 pub fn new(paths: Vec<String>) -> Self {
607 Self {
608 paths,
609 override_config: false,
610 ignore_overrides: false,
611 }
612 }
613
614 pub fn override_config(mut self, value: bool) -> Self {
615 self.override_config = value;
616 self
617 }
618
619 pub fn ignore_overrides(mut self, value: bool) -> Self {
620 self.ignore_overrides = value;
621 self
622 }
623
624 pub fn build(self) -> Result<KittyMessage, CommandError> {
625 let mut payload = Map::new();
626
627 if self.paths.is_empty() {
628 return Err(CommandError::MissingParameter(
629 "paths".to_string(),
630 "load-config".to_string(),
631 ));
632 }
633
634 payload.insert("paths".to_string(), serde_json::json!(self.paths));
635
636 if self.override_config {
637 payload.insert("override".to_string(), serde_json::Value::Bool(true));
638 }
639
640 if self.ignore_overrides {
641 payload.insert(
642 "ignore_overrides".to_string(),
643 serde_json::Value::Bool(true),
644 );
645 }
646
647 Ok(CommandBuilder::new("load-config")
648 .payload(serde_json::Value::Object(payload))
649 .build())
650 }
651}
652
653pub struct ResizeOSWindowCommand {
654 match_spec: Option<String>,
655 self_window: bool,
656 incremental: bool,
657 action: Option<String>,
658 unit: Option<String>,
659 width: Option<i32>,
660 height: Option<i32>,
661}
662
663impl ResizeOSWindowCommand {
664 pub fn new() -> Self {
665 Self {
666 match_spec: None,
667 self_window: false,
668 incremental: false,
669 action: None,
670 unit: None,
671 width: None,
672 height: None,
673 }
674 }
675
676 pub fn match_spec(mut self, spec: impl Into<String>) -> Self {
677 self.match_spec = Some(spec.into());
678 self
679 }
680
681 pub fn self_window(mut self, value: bool) -> Self {
682 self.self_window = value;
683 self
684 }
685
686 pub fn incremental(mut self, value: bool) -> Self {
687 self.incremental = value;
688 self
689 }
690
691 pub fn action(mut self, value: impl Into<String>) -> Self {
692 self.action = Some(value.into());
693 self
694 }
695
696 pub fn unit(mut self, value: impl Into<String>) -> Self {
697 self.unit = Some(value.into());
698 self
699 }
700
701 pub fn width(mut self, value: i32) -> Self {
702 self.width = Some(value);
703 self
704 }
705
706 pub fn height(mut self, value: i32) -> Self {
707 self.height = Some(value);
708 self
709 }
710
711 pub fn build(self) -> Result<KittyMessage, CommandError> {
712 let mut payload = Map::new();
713
714 if let Some(match_spec) = self.match_spec {
715 payload.insert("match".to_string(), serde_json::Value::String(match_spec));
716 }
717
718 if self.self_window {
719 payload.insert("self".to_string(), serde_json::Value::Bool(true));
720 }
721
722 if self.incremental {
723 payload.insert("incremental".to_string(), serde_json::Value::Bool(true));
724 }
725
726 if let Some(action) = self.action {
727 payload.insert("action".to_string(), serde_json::Value::String(action));
728 }
729
730 if let Some(unit) = self.unit {
731 payload.insert("unit".to_string(), serde_json::Value::String(unit));
732 }
733
734 if let Some(width) = self.width {
735 payload.insert("width".to_string(), serde_json::json!(width));
736 }
737
738 if let Some(height) = self.height {
739 payload.insert("height".to_string(), serde_json::json!(height));
740 }
741
742 Ok(CommandBuilder::new("resize-os-window")
743 .payload(serde_json::Value::Object(payload))
744 .build())
745 }
746}
747
748pub struct DisableLigaturesCommand {
749 strategy: Option<String>,
750 match_window: Option<String>,
751 match_tab: Option<String>,
752 all: bool,
753}
754
755impl DisableLigaturesCommand {
756 pub fn new() -> Self {
757 Self {
758 strategy: None,
759 match_window: None,
760 match_tab: None,
761 all: false,
762 }
763 }
764
765 pub fn strategy(mut self, value: impl Into<String>) -> Self {
766 self.strategy = Some(value.into());
767 self
768 }
769
770 pub fn match_window(mut self, spec: impl Into<String>) -> Self {
771 self.match_window = Some(spec.into());
772 self
773 }
774
775 pub fn match_tab(mut self, spec: impl Into<String>) -> Self {
776 self.match_tab = Some(spec.into());
777 self
778 }
779
780 pub fn all(mut self, value: bool) -> Self {
781 self.all = value;
782 self
783 }
784
785 pub fn build(self) -> Result<KittyMessage, CommandError> {
786 let mut payload = Map::new();
787
788 if let Some(strategy) = self.strategy {
789 payload.insert("strategy".to_string(), serde_json::Value::String(strategy));
790 }
791
792 if let Some(match_window) = self.match_window {
793 payload.insert(
794 "match_window".to_string(),
795 serde_json::Value::String(match_window),
796 );
797 }
798
799 if let Some(match_tab) = self.match_tab {
800 payload.insert(
801 "match_tab".to_string(),
802 serde_json::Value::String(match_tab),
803 );
804 }
805
806 if self.all {
807 payload.insert("all".to_string(), serde_json::Value::Bool(true));
808 }
809
810 Ok(CommandBuilder::new("disable-ligatures")
811 .payload(serde_json::Value::Object(payload))
812 .build())
813 }
814}
815
816pub struct SignalChildCommand {
817 signals: Vec<i32>,
818 match_spec: Option<String>,
819}
820
821impl SignalChildCommand {
822 pub fn new(signals: Vec<i32>) -> Self {
823 Self {
824 signals,
825 match_spec: None,
826 }
827 }
828
829 pub fn match_spec(mut self, spec: impl Into<String>) -> Self {
830 self.match_spec = Some(spec.into());
831 self
832 }
833
834 pub fn build(self) -> Result<KittyMessage, CommandError> {
835 let mut payload = Map::new();
836
837 if self.signals.is_empty() {
838 return Err(CommandError::MissingParameter(
839 "signals".to_string(),
840 "signal-child".to_string(),
841 ));
842 }
843
844 payload.insert("signals".to_string(), serde_json::json!(self.signals));
845
846 if let Some(match_spec) = self.match_spec {
847 payload.insert("match".to_string(), serde_json::Value::String(match_spec));
848 }
849
850 Ok(CommandBuilder::new("signal-child")
851 .payload(serde_json::Value::Object(payload))
852 .build())
853 }
854}
855
856#[cfg(test)]
857mod tests {
858 use super::*;
859
860 #[test]
861 fn test_run_basic() {
862 let cmd = RunCommand::new().build();
863 assert!(cmd.is_ok());
864 let msg = cmd.unwrap();
865 assert_eq!(msg.cmd, "run");
866 }
867
868 #[test]
869 fn test_run_with_options() {
870 let cmd = RunCommand::new()
871 .data("test data")
872 .cmdline("bash")
873 .allow_remote_control(true)
874 .build();
875 assert!(cmd.is_ok());
876 let msg = cmd.unwrap();
877 assert_eq!(msg.cmd, "run");
878 }
879
880 #[test]
881 fn test_kitten_basic() {
882 let cmd = KittenCommand::new().build();
883 assert!(cmd.is_ok());
884 let msg = cmd.unwrap();
885 assert_eq!(msg.cmd, "kitten");
886 }
887
888 #[test]
889 fn test_kitten_with_args() {
890 let cmd = KittenCommand::new().args("diff").build();
891 assert!(cmd.is_ok());
892 let msg = cmd.unwrap();
893 assert_eq!(msg.cmd, "kitten");
894 }
895
896 #[test]
897 fn test_launch_basic() {
898 let cmd = LaunchCommand::new().build();
899 assert!(cmd.is_ok());
900 let msg = cmd.unwrap();
901 assert_eq!(msg.cmd, "launch");
902 }
903
904 #[test]
905 fn test_launch_with_options() {
906 let cmd = LaunchCommand::new()
907 .args("bash")
908 .window_title("Test")
909 .cwd("/home")
910 .keep_focus(true)
911 .build();
912 assert!(cmd.is_ok());
913 let msg = cmd.unwrap();
914 assert_eq!(msg.cmd, "launch");
915 }
916
917 #[test]
918 fn test_env_basic() {
919 let mut env_map = Map::new();
920 env_map.insert(
921 "PATH".to_string(),
922 serde_json::Value::String("/usr/bin".to_string()),
923 );
924 let cmd = EnvCommand::new(env_map).build();
925 assert!(cmd.is_ok());
926 let msg = cmd.unwrap();
927 assert_eq!(msg.cmd, "env");
928 }
929
930 #[test]
931 fn test_env_empty() {
932 let cmd = EnvCommand::new(Map::new()).build();
933 assert!(cmd.is_err());
934 if let Err(CommandError::MissingParameter(field, cmd_name)) = cmd {
935 assert_eq!(field, "env");
936 assert_eq!(cmd_name, "env");
937 } else {
938 panic!("Expected MissingParameter error");
939 }
940 }
941
942 #[test]
943 fn test_set_user_vars_basic() {
944 let cmd = SetUserVarsCommand::new(vec!["var1".to_string(), "var2".to_string()]).build();
945 assert!(cmd.is_ok());
946 let msg = cmd.unwrap();
947 assert_eq!(msg.cmd, "set-user-vars");
948 }
949
950 #[test]
951 fn test_set_user_vars_empty() {
952 let cmd = SetUserVarsCommand::new(vec![]).build();
953 assert!(cmd.is_err());
954 if let Err(CommandError::MissingParameter(field, cmd_name)) = cmd {
955 assert_eq!(field, "var");
956 assert_eq!(cmd_name, "set-user-vars");
957 } else {
958 panic!("Expected MissingParameter error");
959 }
960 }
961
962 #[test]
963 fn test_load_config_basic() {
964 let cmd = LoadConfigCommand::new(vec!["kitty.conf".to_string()]).build();
965 assert!(cmd.is_ok());
966 let msg = cmd.unwrap();
967 assert_eq!(msg.cmd, "load-config");
968 }
969
970 #[test]
971 fn test_load_config_empty() {
972 let cmd = LoadConfigCommand::new(vec![]).build();
973 assert!(cmd.is_err());
974 if let Err(CommandError::MissingParameter(field, cmd_name)) = cmd {
975 assert_eq!(field, "paths");
976 assert_eq!(cmd_name, "load-config");
977 } else {
978 panic!("Expected MissingParameter error");
979 }
980 }
981
982 #[test]
983 fn test_resize_os_window_basic() {
984 let cmd = ResizeOSWindowCommand::new().build();
985 assert!(cmd.is_ok());
986 let msg = cmd.unwrap();
987 assert_eq!(msg.cmd, "resize-os-window");
988 }
989
990 #[test]
991 fn test_resize_os_window_with_options() {
992 let cmd = ResizeOSWindowCommand::new()
993 .width(800)
994 .height(600)
995 .unit("px")
996 .build();
997 assert!(cmd.is_ok());
998 let msg = cmd.unwrap();
999 assert_eq!(msg.cmd, "resize-os-window");
1000 }
1001
1002 #[test]
1003 fn test_disable_ligatures_basic() {
1004 let cmd = DisableLigaturesCommand::new().build();
1005 assert!(cmd.is_ok());
1006 let msg = cmd.unwrap();
1007 assert_eq!(msg.cmd, "disable-ligatures");
1008 }
1009
1010 #[test]
1011 fn test_disable_ligatures_with_options() {
1012 let cmd = DisableLigaturesCommand::new()
1013 .strategy("never")
1014 .all(true)
1015 .build();
1016 assert!(cmd.is_ok());
1017 let msg = cmd.unwrap();
1018 assert_eq!(msg.cmd, "disable-ligatures");
1019 }
1020
1021 #[test]
1022 fn test_signal_child_basic() {
1023 let cmd = SignalChildCommand::new(vec![9, 15]).build();
1024 assert!(cmd.is_ok());
1025 let msg = cmd.unwrap();
1026 assert_eq!(msg.cmd, "signal-child");
1027 }
1028
1029 #[test]
1030 fn test_signal_child_empty() {
1031 let cmd = SignalChildCommand::new(vec![]).build();
1032 assert!(cmd.is_err());
1033 if let Err(CommandError::MissingParameter(field, cmd_name)) = cmd {
1034 assert_eq!(field, "signals");
1035 assert_eq!(cmd_name, "signal-child");
1036 } else {
1037 panic!("Expected MissingParameter error");
1038 }
1039 }
1040}