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