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