Skip to main content

kitty_rc/commands/
layout.rs

1use crate::command::CommandBuilder;
2use crate::error::CommandError;
3use crate::protocol::KittyMessage;
4use bon::Builder;
5use serde::Serialize;
6
7fn is_false(v: &bool) -> bool {
8    !v
9}
10
11#[derive(Builder, Serialize)]
12pub struct GotoLayoutCommand {
13    layout: String,
14
15    #[serde(skip_serializing_if = "Option::is_none", rename = "match", default)]
16    match_spec: Option<String>,
17}
18
19impl GotoLayoutCommand {
20    pub fn to_message(self) -> Result<KittyMessage, CommandError> {
21        if self.layout.is_empty() {
22            return Err(CommandError::MissingParameter(
23                "layout".to_string(),
24                "goto-layout".to_string(),
25            ));
26        }
27
28        let payload =
29            serde_json::to_value(self).map_err(|e| CommandError::ValidationError(e.to_string()))?;
30
31        Ok(CommandBuilder::new("goto-layout").payload(payload).build())
32    }
33}
34
35#[derive(Builder, Serialize)]
36pub struct SetEnabledLayoutsCommand {
37    layouts: Vec<String>,
38
39    #[serde(skip_serializing_if = "Option::is_none", rename = "match", default)]
40    match_spec: Option<String>,
41
42    #[builder(default = false)]
43    #[serde(skip_serializing_if = "is_false")]
44    configured: bool,
45}
46
47impl SetEnabledLayoutsCommand {
48    pub fn to_message(self) -> Result<KittyMessage, CommandError> {
49        if self.layouts.is_empty() {
50            return Err(CommandError::MissingParameter(
51                "layouts".to_string(),
52                "set-enabled-layouts".to_string(),
53            ));
54        }
55
56        let payload =
57            serde_json::to_value(self).map_err(|e| CommandError::ValidationError(e.to_string()))?;
58
59        Ok(CommandBuilder::new("set-enabled-layouts")
60            .payload(payload)
61            .build())
62    }
63}
64
65#[derive(Builder, Serialize)]
66pub struct LastUsedLayoutCommand {
67    #[serde(skip_serializing_if = "Option::is_none", rename = "match", default)]
68    match_spec: Option<String>,
69
70    #[builder(default = false)]
71    #[serde(skip_serializing_if = "is_false")]
72    all: bool,
73}
74
75impl LastUsedLayoutCommand {
76    pub fn to_message(self) -> Result<KittyMessage, CommandError> {
77        let payload =
78            serde_json::to_value(self).map_err(|e| CommandError::ValidationError(e.to_string()))?;
79
80        Ok(CommandBuilder::new("last-used-layout")
81            .payload(payload)
82            .build())
83    }
84}
85
86#[cfg(test)]
87mod tests {
88    use super::*;
89
90    #[test]
91    fn test_goto_layout() {
92        let cmd = GotoLayoutCommand::builder()
93            .layout("tall".to_string())
94            .build()
95            .to_message();
96        assert!(cmd.is_ok());
97        let msg = cmd.unwrap();
98        assert_eq!(msg.cmd, "goto-layout");
99        assert!(msg.payload.is_some());
100    }
101
102    #[test]
103    fn test_goto_layout_empty() {
104        let cmd = GotoLayoutCommand::builder()
105            .layout("".to_string())
106            .build()
107            .to_message();
108        assert!(cmd.is_err());
109        if let Err(CommandError::MissingParameter(field, cmd_name)) = cmd {
110            assert_eq!(field, "layout");
111            assert_eq!(cmd_name, "goto-layout");
112        } else {
113            panic!("Expected MissingParameter error");
114        }
115    }
116
117    #[test]
118    fn test_goto_layout_with_match() {
119        let cmd = GotoLayoutCommand::builder()
120            .layout("grid".to_string())
121            .match_spec("id:0".to_string())
122            .build()
123            .to_message();
124        assert!(cmd.is_ok());
125        let msg = cmd.unwrap();
126        assert_eq!(msg.cmd, "goto-layout");
127    }
128
129    #[test]
130    fn test_set_enabled_layouts() {
131        let layouts = vec!["tall".to_string(), "grid".to_string()];
132        let cmd = SetEnabledLayoutsCommand::builder()
133            .layouts(layouts)
134            .build()
135            .to_message();
136        assert!(cmd.is_ok());
137        let msg = cmd.unwrap();
138        assert_eq!(msg.cmd, "set-enabled-layouts");
139        assert!(msg.payload.is_some());
140    }
141
142    #[test]
143    fn test_set_enabled_layouts_empty() {
144        let cmd = SetEnabledLayoutsCommand::builder()
145            .layouts(vec![])
146            .build()
147            .to_message();
148        assert!(cmd.is_err());
149        if let Err(CommandError::MissingParameter(field, cmd_name)) = cmd {
150            assert_eq!(field, "layouts");
151            assert_eq!(cmd_name, "set-enabled-layouts");
152        } else {
153            panic!("Expected MissingParameter error");
154        }
155    }
156
157    #[test]
158    fn test_set_enabled_layouts_with_match() {
159        let layouts = vec!["stack".to_string()];
160        let cmd = SetEnabledLayoutsCommand::builder()
161            .layouts(layouts)
162            .match_spec("id:1".to_string())
163            .build()
164            .to_message();
165        assert!(cmd.is_ok());
166        let msg = cmd.unwrap();
167        assert_eq!(msg.cmd, "set-enabled-layouts");
168    }
169
170    #[test]
171    fn test_set_enabled_layouts_configured() {
172        let layouts = vec!["tall".to_string()];
173        let cmd = SetEnabledLayoutsCommand::builder()
174            .layouts(layouts)
175            .configured(true)
176            .build()
177            .to_message();
178        assert!(cmd.is_ok());
179        let msg = cmd.unwrap();
180        assert_eq!(msg.cmd, "set-enabled-layouts");
181    }
182
183    #[test]
184    fn test_last_used_layout() {
185        let cmd = LastUsedLayoutCommand::builder().build().to_message();
186        assert!(cmd.is_ok());
187        let msg = cmd.unwrap();
188        assert_eq!(msg.cmd, "last-used-layout");
189    }
190
191    #[test]
192    fn test_last_used_layout_with_match() {
193        let cmd = LastUsedLayoutCommand::builder()
194            .match_spec("id:0".to_string())
195            .build()
196            .to_message();
197        assert!(cmd.is_ok());
198        let msg = cmd.unwrap();
199        assert_eq!(msg.cmd, "last-used-layout");
200    }
201
202    #[test]
203    fn test_last_used_layout_all() {
204        let cmd = LastUsedLayoutCommand::builder()
205            .all(true)
206            .build()
207            .to_message();
208        assert!(cmd.is_ok());
209        let msg = cmd.unwrap();
210        assert_eq!(msg.cmd, "last-used-layout");
211    }
212}