kitty_rc/commands/
layout.rs1use 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}