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