Skip to main content

kitty_rc/commands/
layout.rs

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