Skip to main content

kitty_rc/commands/
tab.rs

1use crate::command::CommandBuilder;
2use crate::error::CommandError;
3use crate::protocol::KittyMessage;
4
5pub struct FocusTabCommand {
6    match_spec: Option<String>,
7}
8
9impl FocusTabCommand {
10    pub fn new() -> Self {
11        Self { match_spec: None }
12    }
13
14    pub fn match_spec(mut self, spec: impl Into<String>) -> Self {
15        self.match_spec = Some(spec.into());
16        self
17    }
18
19    pub fn build(self) -> Result<KittyMessage, CommandError> {
20        let mut payload = serde_json::Map::new();
21
22        if let Some(match_spec) = self.match_spec {
23            payload.insert("match".to_string(), serde_json::Value::String(match_spec));
24        }
25
26        Ok(CommandBuilder::new("focus-tab")
27            .payload(serde_json::Value::Object(payload))
28            .build())
29    }
30}
31
32pub struct SetTabTitleCommand {
33    title: String,
34    match_spec: Option<String>,
35}
36
37impl SetTabTitleCommand {
38    pub fn new(title: impl Into<String>) -> Self {
39        Self {
40            title: title.into(),
41            match_spec: None,
42        }
43    }
44
45    pub fn match_spec(mut self, spec: impl Into<String>) -> Self {
46        self.match_spec = Some(spec.into());
47        self
48    }
49
50    pub fn build(self) -> Result<KittyMessage, CommandError> {
51        let mut payload = serde_json::Map::new();
52
53        if self.title.is_empty() {
54            return Err(CommandError::MissingParameter(
55                "title".to_string(),
56                "set-tab-title".to_string(),
57            ));
58        }
59
60        payload.insert("title".to_string(), serde_json::Value::String(self.title));
61
62        if let Some(match_spec) = self.match_spec {
63            payload.insert("match".to_string(), serde_json::Value::String(match_spec));
64        }
65
66        Ok(CommandBuilder::new("set-tab-title")
67            .payload(serde_json::Value::Object(payload))
68            .build())
69    }
70}
71
72pub struct CloseTabCommand {
73    match_spec: Option<String>,
74    self_tab: bool,
75    ignore_no_match: bool,
76}
77
78impl CloseTabCommand {
79    pub fn new() -> Self {
80        Self {
81            match_spec: None,
82            self_tab: false,
83            ignore_no_match: false,
84        }
85    }
86
87    pub fn match_spec(mut self, spec: impl Into<String>) -> Self {
88        self.match_spec = Some(spec.into());
89        self
90    }
91
92    pub fn self_tab(mut self, value: bool) -> Self {
93        self.self_tab = value;
94        self
95    }
96
97    pub fn ignore_no_match(mut self, value: bool) -> Self {
98        self.ignore_no_match = value;
99        self
100    }
101
102    pub fn build(self) -> Result<KittyMessage, CommandError> {
103        let mut payload = serde_json::Map::new();
104
105        if let Some(match_spec) = self.match_spec {
106            payload.insert("match".to_string(), serde_json::Value::String(match_spec));
107        }
108
109        if self.self_tab {
110            payload.insert("self".to_string(), serde_json::Value::Bool(true));
111        }
112
113        if self.ignore_no_match {
114            payload.insert("ignore_no_match".to_string(), serde_json::Value::Bool(true));
115        }
116
117        Ok(CommandBuilder::new("close-tab")
118            .payload(serde_json::Value::Object(payload))
119            .build())
120    }
121}
122
123pub struct DetachTabCommand {
124    match_spec: Option<String>,
125    target_tab: Option<String>,
126    self_tab: bool,
127}
128
129impl DetachTabCommand {
130    pub fn new() -> Self {
131        Self {
132            match_spec: None,
133            target_tab: None,
134            self_tab: false,
135        }
136    }
137
138    pub fn match_spec(mut self, spec: impl Into<String>) -> Self {
139        self.match_spec = Some(spec.into());
140        self
141    }
142
143    pub fn target_tab(mut self, spec: impl Into<String>) -> Self {
144        self.target_tab = Some(spec.into());
145        self
146    }
147
148    pub fn self_tab(mut self, value: bool) -> Self {
149        self.self_tab = value;
150        self
151    }
152
153    pub fn build(self) -> Result<KittyMessage, CommandError> {
154        let mut payload = serde_json::Map::new();
155
156        if let Some(match_spec) = self.match_spec {
157            payload.insert("match".to_string(), serde_json::Value::String(match_spec));
158        }
159
160        if let Some(target_tab) = self.target_tab {
161            payload.insert(
162                "target_tab".to_string(),
163                serde_json::Value::String(target_tab),
164            );
165        }
166
167        if self.self_tab {
168            payload.insert("self".to_string(), serde_json::Value::Bool(true));
169        }
170
171        Ok(CommandBuilder::new("detach-tab")
172            .payload(serde_json::Value::Object(payload))
173            .build())
174    }
175}
176
177#[cfg(test)]
178mod tests {
179    use super::*;
180
181    #[test]
182    fn test_focus_tab_basic() {
183        let cmd = FocusTabCommand::new().build();
184        assert!(cmd.is_ok());
185        let msg = cmd.unwrap();
186        assert_eq!(msg.cmd, "focus-tab");
187    }
188
189    #[test]
190    fn test_focus_tab_with_match() {
191        let cmd = FocusTabCommand::new().match_spec("id:0").build();
192        assert!(cmd.is_ok());
193        let msg = cmd.unwrap();
194        assert_eq!(msg.cmd, "focus-tab");
195        assert!(msg.payload.is_some());
196    }
197
198    #[test]
199    fn test_set_tab_title() {
200        let cmd = SetTabTitleCommand::new("My Tab").build();
201        assert!(cmd.is_ok());
202        let msg = cmd.unwrap();
203        assert_eq!(msg.cmd, "set-tab-title");
204        assert!(msg.payload.is_some());
205    }
206
207    #[test]
208    fn test_set_tab_title_empty() {
209        let cmd = SetTabTitleCommand::new("").build();
210        assert!(cmd.is_err());
211        if let Err(CommandError::MissingParameter(field, cmd_name)) = cmd {
212            assert_eq!(field, "title");
213            assert_eq!(cmd_name, "set-tab-title");
214        } else {
215            panic!("Expected MissingParameter error");
216        }
217    }
218
219    #[test]
220    fn test_set_tab_title_with_match() {
221        let cmd = SetTabTitleCommand::new("New Title")
222            .match_spec("id:1")
223            .build();
224        assert!(cmd.is_ok());
225        let msg = cmd.unwrap();
226        assert_eq!(msg.cmd, "set-tab-title");
227    }
228
229    #[test]
230    fn test_close_tab_basic() {
231        let cmd = CloseTabCommand::new().build();
232        assert!(cmd.is_ok());
233        let msg = cmd.unwrap();
234        assert_eq!(msg.cmd, "close-tab");
235    }
236
237    #[test]
238    fn test_close_tab_with_match() {
239        let cmd = CloseTabCommand::new().match_spec("id:2").build();
240        assert!(cmd.is_ok());
241        let msg = cmd.unwrap();
242        assert_eq!(msg.cmd, "close-tab");
243    }
244
245    #[test]
246    fn test_close_tab_self() {
247        let cmd = CloseTabCommand::new().self_tab(true).build();
248        assert!(cmd.is_ok());
249        let msg = cmd.unwrap();
250        assert_eq!(msg.cmd, "close-tab");
251    }
252
253    #[test]
254    fn test_close_tab_ignore_no_match() {
255        let cmd = CloseTabCommand::new().ignore_no_match(true).build();
256        assert!(cmd.is_ok());
257        let msg = cmd.unwrap();
258        assert_eq!(msg.cmd, "close-tab");
259    }
260
261    #[test]
262    fn test_detach_tab_basic() {
263        let cmd = DetachTabCommand::new().build();
264        assert!(cmd.is_ok());
265        let msg = cmd.unwrap();
266        assert_eq!(msg.cmd, "detach-tab");
267    }
268
269    #[test]
270    fn test_detach_tab_with_match() {
271        let cmd = DetachTabCommand::new().match_spec("id:0").build();
272        assert!(cmd.is_ok());
273        let msg = cmd.unwrap();
274        assert_eq!(msg.cmd, "detach-tab");
275    }
276
277    #[test]
278    fn test_detach_tab_with_target_tab() {
279        let cmd = DetachTabCommand::new().target_tab("id:1").build();
280        assert!(cmd.is_ok());
281        let msg = cmd.unwrap();
282        assert_eq!(msg.cmd, "detach-tab");
283    }
284
285    #[test]
286    fn test_detach_tab_self() {
287        let cmd = DetachTabCommand::new().self_tab(true).build();
288        assert!(cmd.is_ok());
289        let msg = cmd.unwrap();
290        assert_eq!(msg.cmd, "detach-tab");
291    }
292
293    #[test]
294    fn test_detach_tab_all_options() {
295        let cmd = DetachTabCommand::new()
296            .match_spec("id:0")
297            .target_tab("id:1")
298            .self_tab(true)
299            .build();
300        assert!(cmd.is_ok());
301        let msg = cmd.unwrap();
302        assert_eq!(msg.cmd, "detach-tab");
303    }
304}