1use crate::command::CommandBuilder;
2use crate::error::CommandError;
3use crate::protocol::KittyMessage;
4use serde_json::Map;
5
6pub struct SetBackgroundOpacityCommand {
7 opacity: f32,
8 match_window: Option<String>,
9 match_tab: Option<String>,
10 all: bool,
11 toggle: bool,
12}
13
14impl SetBackgroundOpacityCommand {
15 pub fn new(opacity: f32) -> Self {
16 Self {
17 opacity,
18 match_window: None,
19 match_tab: None,
20 all: false,
21 toggle: false,
22 }
23 }
24
25 pub fn match_window(mut self, spec: impl Into<String>) -> Self {
26 self.match_window = Some(spec.into());
27 self
28 }
29
30 pub fn match_tab(mut self, spec: impl Into<String>) -> Self {
31 self.match_tab = Some(spec.into());
32 self
33 }
34
35 pub fn all(mut self, value: bool) -> Self {
36 self.all = value;
37 self
38 }
39
40 pub fn toggle(mut self, value: bool) -> Self {
41 self.toggle = value;
42 self
43 }
44
45 pub fn build(self) -> Result<KittyMessage, CommandError> {
46 let mut payload = Map::new();
47
48 if self.opacity < 0.0 || self.opacity > 1.0 {
49 return Err(CommandError::ValidationError(
50 "opacity must be between 0.0 and 1.0".to_string(),
51 ));
52 }
53
54 payload.insert("opacity".to_string(), serde_json::json!(self.opacity));
55
56 if let Some(match_window) = self.match_window {
57 payload.insert(
58 "match_window".to_string(),
59 serde_json::Value::String(match_window),
60 );
61 }
62
63 if let Some(match_tab) = self.match_tab {
64 payload.insert(
65 "match_tab".to_string(),
66 serde_json::Value::String(match_tab),
67 );
68 }
69
70 if self.all {
71 payload.insert("all".to_string(), serde_json::Value::Bool(true));
72 }
73
74 if self.toggle {
75 payload.insert("toggle".to_string(), serde_json::Value::Bool(true));
76 }
77
78 Ok(CommandBuilder::new("set-background-opacity")
79 .payload(serde_json::Value::Object(payload))
80 .build())
81 }
82}
83
84pub struct SetBackgroundImageCommand {
85 data: String,
86 match_spec: Option<String>,
87 layout: Option<String>,
88 all: bool,
89 configured: bool,
90}
91
92impl SetBackgroundImageCommand {
93 pub fn new(data: impl Into<String>) -> Self {
94 Self {
95 data: data.into(),
96 match_spec: None,
97 layout: None,
98 all: false,
99 configured: false,
100 }
101 }
102
103 pub fn match_spec(mut self, spec: impl Into<String>) -> Self {
104 self.match_spec = Some(spec.into());
105 self
106 }
107
108 pub fn layout(mut self, value: impl Into<String>) -> Self {
109 self.layout = Some(value.into());
110 self
111 }
112
113 pub fn all(mut self, value: bool) -> Self {
114 self.all = value;
115 self
116 }
117
118 pub fn configured(mut self, value: bool) -> Self {
119 self.configured = value;
120 self
121 }
122
123 pub fn build(self) -> Result<KittyMessage, CommandError> {
124 let mut payload = Map::new();
125
126 if self.data.is_empty() {
127 return Err(CommandError::MissingParameter(
128 "data".to_string(),
129 "set-background-image".to_string(),
130 ));
131 }
132
133 payload.insert("data".to_string(), serde_json::Value::String(self.data));
134
135 if let Some(match_spec) = self.match_spec {
136 payload.insert("match".to_string(), serde_json::Value::String(match_spec));
137 }
138
139 if let Some(layout) = self.layout {
140 payload.insert("layout".to_string(), serde_json::Value::String(layout));
141 }
142
143 if self.all {
144 payload.insert("all".to_string(), serde_json::Value::Bool(true));
145 }
146
147 if self.configured {
148 payload.insert("configured".to_string(), serde_json::Value::Bool(true));
149 }
150
151 Ok(CommandBuilder::new("set-background-image")
152 .payload(serde_json::Value::Object(payload))
153 .build())
154 }
155}
156
157pub struct SetColorsCommand {
158 colors: Map<String, serde_json::Value>,
159 match_window: Option<String>,
160 match_tab: Option<String>,
161 all: bool,
162 configured: bool,
163 reset: bool,
164}
165
166impl SetColorsCommand {
167 pub fn new(colors: Map<String, serde_json::Value>) -> Self {
168 Self {
169 colors,
170 match_window: None,
171 match_tab: None,
172 all: false,
173 configured: false,
174 reset: false,
175 }
176 }
177
178 pub fn match_window(mut self, spec: impl Into<String>) -> Self {
179 self.match_window = Some(spec.into());
180 self
181 }
182
183 pub fn match_tab(mut self, spec: impl Into<String>) -> Self {
184 self.match_tab = Some(spec.into());
185 self
186 }
187
188 pub fn all(mut self, value: bool) -> Self {
189 self.all = value;
190 self
191 }
192
193 pub fn configured(mut self, value: bool) -> Self {
194 self.configured = value;
195 self
196 }
197
198 pub fn reset(mut self, value: bool) -> Self {
199 self.reset = value;
200 self
201 }
202
203 pub fn build(self) -> Result<KittyMessage, CommandError> {
204 let mut payload = Map::new();
205
206 if self.colors.is_empty() {
207 return Err(CommandError::MissingParameter(
208 "colors".to_string(),
209 "set-colors".to_string(),
210 ));
211 }
212
213 payload.insert("colors".to_string(), serde_json::Value::Object(self.colors));
214
215 if let Some(match_window) = self.match_window {
216 payload.insert(
217 "match_window".to_string(),
218 serde_json::Value::String(match_window),
219 );
220 }
221
222 if let Some(match_tab) = self.match_tab {
223 payload.insert(
224 "match_tab".to_string(),
225 serde_json::Value::String(match_tab),
226 );
227 }
228
229 if self.all {
230 payload.insert("all".to_string(), serde_json::Value::Bool(true));
231 }
232
233 if self.configured {
234 payload.insert("configured".to_string(), serde_json::Value::Bool(true));
235 }
236
237 if self.reset {
238 payload.insert("reset".to_string(), serde_json::Value::Bool(true));
239 }
240
241 Ok(CommandBuilder::new("set-colors")
242 .payload(serde_json::Value::Object(payload))
243 .build())
244 }
245}
246
247pub struct SetFontSizeCommand {
248 size: i32,
249 all: bool,
250 increment_op: Option<String>,
251}
252
253impl SetFontSizeCommand {
254 pub fn new(size: i32) -> Self {
255 Self {
256 size,
257 all: false,
258 increment_op: None,
259 }
260 }
261
262 pub fn all(mut self, value: bool) -> Self {
263 self.all = value;
264 self
265 }
266
267 pub fn increment_op(mut self, value: impl Into<String>) -> Self {
268 self.increment_op = Some(value.into());
269 self
270 }
271
272 pub fn build(self) -> Result<KittyMessage, CommandError> {
273 let mut payload = Map::new();
274
275 payload.insert("size".to_string(), serde_json::json!(self.size));
276
277 if self.all {
278 payload.insert("all".to_string(), serde_json::Value::Bool(true));
279 }
280
281 if let Some(increment_op) = self.increment_op {
282 payload.insert(
283 "increment_op".to_string(),
284 serde_json::Value::String(increment_op),
285 );
286 }
287
288 Ok(CommandBuilder::new("set-font-size")
289 .payload(serde_json::Value::Object(payload))
290 .build())
291 }
292}
293
294pub struct SetSpacingCommand {
295 settings: Map<String, serde_json::Value>,
296 match_window: Option<String>,
297 match_tab: Option<String>,
298 all: bool,
299 configured: bool,
300}
301
302impl SetSpacingCommand {
303 pub fn new(settings: Map<String, serde_json::Value>) -> Self {
304 Self {
305 settings,
306 match_window: None,
307 match_tab: None,
308 all: false,
309 configured: false,
310 }
311 }
312
313 pub fn match_window(mut self, spec: impl Into<String>) -> Self {
314 self.match_window = Some(spec.into());
315 self
316 }
317
318 pub fn match_tab(mut self, spec: impl Into<String>) -> Self {
319 self.match_tab = Some(spec.into());
320 self
321 }
322
323 pub fn all(mut self, value: bool) -> Self {
324 self.all = value;
325 self
326 }
327
328 pub fn configured(mut self, value: bool) -> Self {
329 self.configured = value;
330 self
331 }
332
333 pub fn build(self) -> Result<KittyMessage, CommandError> {
334 let mut payload = Map::new();
335
336 if self.settings.is_empty() {
337 return Err(CommandError::MissingParameter(
338 "settings".to_string(),
339 "set-spacing".to_string(),
340 ));
341 }
342
343 payload.insert(
344 "settings".to_string(),
345 serde_json::Value::Object(self.settings),
346 );
347
348 if let Some(match_window) = self.match_window {
349 payload.insert(
350 "match_window".to_string(),
351 serde_json::Value::String(match_window),
352 );
353 }
354
355 if let Some(match_tab) = self.match_tab {
356 payload.insert(
357 "match_tab".to_string(),
358 serde_json::Value::String(match_tab),
359 );
360 }
361
362 if self.all {
363 payload.insert("all".to_string(), serde_json::Value::Bool(true));
364 }
365
366 if self.configured {
367 payload.insert("configured".to_string(), serde_json::Value::Bool(true));
368 }
369
370 Ok(CommandBuilder::new("set-spacing")
371 .payload(serde_json::Value::Object(payload))
372 .build())
373 }
374}
375
376pub struct SetTabColorCommand {
377 colors: Map<String, serde_json::Value>,
378 match_spec: Option<String>,
379 self_tab: bool,
380}
381
382impl SetTabColorCommand {
383 pub fn new(colors: Map<String, serde_json::Value>) -> Self {
384 Self {
385 colors,
386 match_spec: None,
387 self_tab: false,
388 }
389 }
390
391 pub fn match_spec(mut self, spec: impl Into<String>) -> Self {
392 self.match_spec = Some(spec.into());
393 self
394 }
395
396 pub fn self_tab(mut self, value: bool) -> Self {
397 self.self_tab = value;
398 self
399 }
400
401 pub fn build(self) -> Result<KittyMessage, CommandError> {
402 let mut payload = Map::new();
403
404 if self.colors.is_empty() {
405 return Err(CommandError::MissingParameter(
406 "colors".to_string(),
407 "set-tab-color".to_string(),
408 ));
409 }
410
411 payload.insert("colors".to_string(), serde_json::Value::Object(self.colors));
412
413 if let Some(match_spec) = self.match_spec {
414 payload.insert("match".to_string(), serde_json::Value::String(match_spec));
415 }
416
417 if self.self_tab {
418 payload.insert("self".to_string(), serde_json::Value::Bool(true));
419 }
420
421 Ok(CommandBuilder::new("set-tab-color")
422 .payload(serde_json::Value::Object(payload))
423 .build())
424 }
425}
426
427pub struct GetColorsCommand {
428 match_spec: Option<String>,
429 configured: bool,
430}
431
432impl GetColorsCommand {
433 pub fn new() -> Self {
434 Self {
435 match_spec: None,
436 configured: false,
437 }
438 }
439
440 pub fn match_spec(mut self, spec: impl Into<String>) -> Self {
441 self.match_spec = Some(spec.into());
442 self
443 }
444
445 pub fn configured(mut self, value: bool) -> Self {
446 self.configured = value;
447 self
448 }
449
450 pub fn build(self) -> Result<KittyMessage, CommandError> {
451 let mut payload = Map::new();
452
453 if let Some(match_spec) = self.match_spec {
454 payload.insert("match".to_string(), serde_json::Value::String(match_spec));
455 }
456
457 if self.configured {
458 payload.insert("configured".to_string(), serde_json::Value::Bool(true));
459 }
460
461 Ok(CommandBuilder::new("get-colors")
462 .payload(serde_json::Value::Object(payload))
463 .build())
464 }
465}
466
467#[cfg(test)]
468mod tests {
469 use super::*;
470
471 #[test]
472 fn test_set_background_opacity_basic() {
473 let cmd = SetBackgroundOpacityCommand::new(0.5).build();
474 assert!(cmd.is_ok());
475 let msg = cmd.unwrap();
476 assert_eq!(msg.cmd, "set-background-opacity");
477 }
478
479 #[test]
480 fn test_set_background_opacity_invalid() {
481 let cmd = SetBackgroundOpacityCommand::new(1.5).build();
482 assert!(cmd.is_err());
483 if let Err(CommandError::ValidationError(msg)) = cmd {
484 assert!(msg.contains("opacity"));
485 } else {
486 panic!("Expected ValidationError error");
487 }
488 }
489
490 #[test]
491 fn test_set_background_opacity_with_options() {
492 let cmd = SetBackgroundOpacityCommand::new(0.8)
493 .all(true)
494 .toggle(true)
495 .build();
496 assert!(cmd.is_ok());
497 let msg = cmd.unwrap();
498 assert_eq!(msg.cmd, "set-background-opacity");
499 }
500
501 #[test]
502 fn test_set_background_image_basic() {
503 let cmd = SetBackgroundImageCommand::new("base64data").build();
504 assert!(cmd.is_ok());
505 let msg = cmd.unwrap();
506 assert_eq!(msg.cmd, "set-background-image");
507 }
508
509 #[test]
510 fn test_set_background_image_empty() {
511 let cmd = SetBackgroundImageCommand::new("").build();
512 assert!(cmd.is_err());
513 if let Err(CommandError::MissingParameter(field, cmd_name)) = cmd {
514 assert_eq!(field, "data");
515 assert_eq!(cmd_name, "set-background-image");
516 } else {
517 panic!("Expected MissingParameter error");
518 }
519 }
520
521 #[test]
522 fn test_set_background_image_with_options() {
523 let cmd = SetBackgroundImageCommand::new("base64data")
524 .layout("tiled")
525 .all(true)
526 .build();
527 assert!(cmd.is_ok());
528 let msg = cmd.unwrap();
529 assert_eq!(msg.cmd, "set-background-image");
530 }
531
532 #[test]
533 fn test_set_colors_basic() {
534 let mut colors = Map::new();
535 colors.insert(
536 "foreground".to_string(),
537 serde_json::Value::String("#ffffff".to_string()),
538 );
539 let cmd = SetColorsCommand::new(colors).build();
540 assert!(cmd.is_ok());
541 let msg = cmd.unwrap();
542 assert_eq!(msg.cmd, "set-colors");
543 }
544
545 #[test]
546 fn test_set_colors_empty() {
547 let cmd = SetColorsCommand::new(Map::new()).build();
548 assert!(cmd.is_err());
549 if let Err(CommandError::MissingParameter(field, cmd_name)) = cmd {
550 assert_eq!(field, "colors");
551 assert_eq!(cmd_name, "set-colors");
552 } else {
553 panic!("Expected MissingParameter error");
554 }
555 }
556
557 #[test]
558 fn test_set_colors_with_options() {
559 let mut colors = Map::new();
560 colors.insert(
561 "background".to_string(),
562 serde_json::Value::String("#000000".to_string()),
563 );
564 let cmd = SetColorsCommand::new(colors).all(true).reset(true).build();
565 assert!(cmd.is_ok());
566 let msg = cmd.unwrap();
567 assert_eq!(msg.cmd, "set-colors");
568 }
569
570 #[test]
571 fn test_set_font_size_basic() {
572 let cmd = SetFontSizeCommand::new(14).build();
573 assert!(cmd.is_ok());
574 let msg = cmd.unwrap();
575 assert_eq!(msg.cmd, "set-font-size");
576 }
577
578 #[test]
579 fn test_set_font_size_with_options() {
580 let cmd = SetFontSizeCommand::new(16)
581 .all(true)
582 .increment_op("set")
583 .build();
584 assert!(cmd.is_ok());
585 let msg = cmd.unwrap();
586 assert_eq!(msg.cmd, "set-font-size");
587 }
588
589 #[test]
590 fn test_set_spacing_basic() {
591 let mut settings = Map::new();
592 settings.insert("padding".to_string(), serde_json::json!(10));
593 let cmd = SetSpacingCommand::new(settings).build();
594 assert!(cmd.is_ok());
595 let msg = cmd.unwrap();
596 assert_eq!(msg.cmd, "set-spacing");
597 }
598
599 #[test]
600 fn test_set_spacing_empty() {
601 let cmd = SetSpacingCommand::new(Map::new()).build();
602 assert!(cmd.is_err());
603 if let Err(CommandError::MissingParameter(field, cmd_name)) = cmd {
604 assert_eq!(field, "settings");
605 assert_eq!(cmd_name, "set-spacing");
606 } else {
607 panic!("Expected MissingParameter error");
608 }
609 }
610
611 #[test]
612 fn test_set_spacing_with_options() {
613 let mut settings = Map::new();
614 settings.insert("margin".to_string(), serde_json::json!(5));
615 let cmd = SetSpacingCommand::new(settings)
616 .all(true)
617 .configured(true)
618 .build();
619 assert!(cmd.is_ok());
620 let msg = cmd.unwrap();
621 assert_eq!(msg.cmd, "set-spacing");
622 }
623
624 #[test]
625 fn test_set_tab_color_basic() {
626 let mut colors = Map::new();
627 colors.insert(
628 "active_tab_foreground".to_string(),
629 serde_json::Value::String("#ffffff".to_string()),
630 );
631 let cmd = SetTabColorCommand::new(colors).build();
632 assert!(cmd.is_ok());
633 let msg = cmd.unwrap();
634 assert_eq!(msg.cmd, "set-tab-color");
635 }
636
637 #[test]
638 fn test_set_tab_color_empty() {
639 let cmd = SetTabColorCommand::new(Map::new()).build();
640 assert!(cmd.is_err());
641 if let Err(CommandError::MissingParameter(field, cmd_name)) = cmd {
642 assert_eq!(field, "colors");
643 assert_eq!(cmd_name, "set-tab-color");
644 } else {
645 panic!("Expected MissingParameter error");
646 }
647 }
648
649 #[test]
650 fn test_set_tab_color_with_options() {
651 let mut colors = Map::new();
652 colors.insert(
653 "active_tab_background".to_string(),
654 serde_json::Value::String("#000000".to_string()),
655 );
656 let cmd = SetTabColorCommand::new(colors).self_tab(true).build();
657 assert!(cmd.is_ok());
658 let msg = cmd.unwrap();
659 assert_eq!(msg.cmd, "set-tab-color");
660 }
661
662 #[test]
663 fn test_get_colors_basic() {
664 let cmd = GetColorsCommand::new().build();
665 assert!(cmd.is_ok());
666 let msg = cmd.unwrap();
667 assert_eq!(msg.cmd, "get-colors");
668 }
669
670 #[test]
671 fn test_get_colors_with_options() {
672 let cmd = GetColorsCommand::new()
673 .match_spec("id:1")
674 .configured(true)
675 .build();
676 assert!(cmd.is_ok());
677 let msg = cmd.unwrap();
678 assert_eq!(msg.cmd, "get-colors");
679 }
680}