Skip to main content

kitty_rc/commands/
style.rs

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}