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("title".to_string(), "set-tab-title".to_string()));
55        }
56
57        payload.insert("title".to_string(), serde_json::Value::String(self.title));
58
59        if let Some(match_spec) = self.match_spec {
60            payload.insert("match".to_string(), serde_json::Value::String(match_spec));
61        }
62
63        Ok(CommandBuilder::new("set-tab-title")
64            .payload(serde_json::Value::Object(payload))
65            .build())
66    }
67}
68
69pub struct CloseTabCommand {
70    match_spec: Option<String>,
71    self_tab: bool,
72    ignore_no_match: bool,
73}
74
75impl CloseTabCommand {
76    pub fn new() -> Self {
77        Self {
78            match_spec: None,
79            self_tab: false,
80            ignore_no_match: false,
81        }
82    }
83
84    pub fn match_spec(mut self, spec: impl Into<String>) -> Self {
85        self.match_spec = Some(spec.into());
86        self
87    }
88
89    pub fn self_tab(mut self, value: bool) -> Self {
90        self.self_tab = value;
91        self
92    }
93
94    pub fn ignore_no_match(mut self, value: bool) -> Self {
95        self.ignore_no_match = value;
96        self
97    }
98
99    pub fn build(self) -> Result<KittyMessage, CommandError> {
100        let mut payload = serde_json::Map::new();
101
102        if let Some(match_spec) = self.match_spec {
103            payload.insert("match".to_string(), serde_json::Value::String(match_spec));
104        }
105
106        if self.self_tab {
107            payload.insert("self".to_string(), serde_json::Value::Bool(true));
108        }
109
110        if self.ignore_no_match {
111            payload.insert("ignore_no_match".to_string(), serde_json::Value::Bool(true));
112        }
113
114        Ok(CommandBuilder::new("close-tab")
115            .payload(serde_json::Value::Object(payload))
116            .build())
117    }
118}
119
120pub struct DetachTabCommand {
121    match_spec: Option<String>,
122    target_tab: Option<String>,
123    self_tab: bool,
124}
125
126impl DetachTabCommand {
127    pub fn new() -> Self {
128        Self {
129            match_spec: None,
130            target_tab: None,
131            self_tab: false,
132        }
133    }
134
135    pub fn match_spec(mut self, spec: impl Into<String>) -> Self {
136        self.match_spec = Some(spec.into());
137        self
138    }
139
140    pub fn target_tab(mut self, spec: impl Into<String>) -> Self {
141        self.target_tab = Some(spec.into());
142        self
143    }
144
145    pub fn self_tab(mut self, value: bool) -> Self {
146        self.self_tab = value;
147        self
148    }
149
150    pub fn build(self) -> Result<KittyMessage, CommandError> {
151        let mut payload = serde_json::Map::new();
152
153        if let Some(match_spec) = self.match_spec {
154            payload.insert("match".to_string(), serde_json::Value::String(match_spec));
155        }
156
157        if let Some(target_tab) = self.target_tab {
158            payload.insert("target_tab".to_string(), serde_json::Value::String(target_tab));
159        }
160
161        if self.self_tab {
162            payload.insert("self".to_string(), serde_json::Value::Bool(true));
163        }
164
165        Ok(CommandBuilder::new("detach-tab")
166            .payload(serde_json::Value::Object(payload))
167            .build())
168    }
169}
170
171#[cfg(test)]
172mod tests {
173    use super::*;
174
175    #[test]
176    fn test_focus_tab_basic() {
177        let cmd = FocusTabCommand::new().build();
178        assert!(cmd.is_ok());
179        let msg = cmd.unwrap();
180        assert_eq!(msg.cmd, "focus-tab");
181    }
182
183    #[test]
184    fn test_focus_tab_with_match() {
185        let cmd = FocusTabCommand::new().match_spec("id:0").build();
186        assert!(cmd.is_ok());
187        let msg = cmd.unwrap();
188        assert_eq!(msg.cmd, "focus-tab");
189        assert!(msg.payload.is_some());
190    }
191
192    #[test]
193    fn test_set_tab_title() {
194        let cmd = SetTabTitleCommand::new("My Tab").build();
195        assert!(cmd.is_ok());
196        let msg = cmd.unwrap();
197        assert_eq!(msg.cmd, "set-tab-title");
198        assert!(msg.payload.is_some());
199    }
200
201    #[test]
202    fn test_set_tab_title_empty() {
203        let cmd = SetTabTitleCommand::new("").build();
204        assert!(cmd.is_err());
205        if let Err(CommandError::MissingParameter(field, cmd_name)) = cmd {
206            assert_eq!(field, "title");
207            assert_eq!(cmd_name, "set-tab-title");
208        } else {
209            panic!("Expected MissingParameter error");
210        }
211    }
212
213    #[test]
214    fn test_set_tab_title_with_match() {
215        let cmd = SetTabTitleCommand::new("New Title").match_spec("id:1").build();
216        assert!(cmd.is_ok());
217        let msg = cmd.unwrap();
218        assert_eq!(msg.cmd, "set-tab-title");
219    }
220
221    #[test]
222    fn test_close_tab_basic() {
223        let cmd = CloseTabCommand::new().build();
224        assert!(cmd.is_ok());
225        let msg = cmd.unwrap();
226        assert_eq!(msg.cmd, "close-tab");
227    }
228
229    #[test]
230    fn test_close_tab_with_match() {
231        let cmd = CloseTabCommand::new().match_spec("id:2").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_self() {
239        let cmd = CloseTabCommand::new().self_tab(true).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_ignore_no_match() {
247        let cmd = CloseTabCommand::new().ignore_no_match(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_detach_tab_basic() {
255        let cmd = DetachTabCommand::new().build();
256        assert!(cmd.is_ok());
257        let msg = cmd.unwrap();
258        assert_eq!(msg.cmd, "detach-tab");
259    }
260
261    #[test]
262    fn test_detach_tab_with_match() {
263        let cmd = DetachTabCommand::new().match_spec("id:0").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_target_tab() {
271        let cmd = DetachTabCommand::new().target_tab("id:1").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_self() {
279        let cmd = DetachTabCommand::new().self_tab(true).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_all_options() {
287        let cmd = DetachTabCommand::new()
288            .match_spec("id:0")
289            .target_tab("id:1")
290            .self_tab(true)
291            .build();
292        assert!(cmd.is_ok());
293        let msg = cmd.unwrap();
294        assert_eq!(msg.cmd, "detach-tab");
295    }
296}