1use std::path::PathBuf;
2
3use serde::Deserialize;
4
5#[derive(Clone, Copy, Debug, Deserialize, PartialEq)]
6pub struct WindowId(pub u32);
7
8impl std::fmt::Display for WindowId {
9 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
10 self.0.fmt(f)
11 }
12}
13
14#[derive(Clone, Copy, Debug, Deserialize, PartialEq)]
15pub struct TabId(pub u32);
16
17#[derive(Clone, Copy, Debug, Deserialize, PartialEq)]
18pub struct OsWindowId(pub u32);
19
20#[derive(Clone, Debug, Deserialize, PartialEq)]
21pub struct OsWindows(pub Vec<OsWindow>);
22
23impl IntoIterator for OsWindows {
24 type Item = OsWindow;
25
26 type IntoIter = std::vec::IntoIter<Self::Item>;
27
28 fn into_iter(self) -> Self::IntoIter {
29 self.0.into_iter()
30 }
31}
32
33#[derive(Clone, Debug, Deserialize, PartialEq)]
34pub struct OsWindow {
35 pub id: OsWindowId,
36 pub is_active: bool,
37 pub is_focused: bool,
38 pub tabs: Vec<Tab>,
39}
40
41#[derive(Clone, Debug, Deserialize, PartialEq)]
42pub struct Tab {
43 pub id: TabId,
44 pub is_active: bool,
45 pub is_focused: bool,
46 pub windows: Vec<Window>,
47}
48
49#[derive(Clone, Debug, Deserialize, PartialEq)]
50pub struct Window {
51 pub id: WindowId,
52 pub is_active: bool,
53 pub is_focused: bool,
54 pub foreground_processes: Vec<Process>,
55}
56
57#[derive(Clone, Debug, Deserialize, PartialEq)]
58pub struct Process {
59 pub pid: u32,
60 pub cwd: Option<PathBuf>,
61 pub cmdline: Vec<String>,
62}
63
64#[cfg(test)]
65mod test {
66 use pretty_assertions::assert_eq;
67
68 use super::{test_fixture::LS_OUTPUT, test_fixture::LS_OUTPUT_JSON, OsWindows};
69
70 #[test]
71 fn test_kitty_ls_output_can_be_deserialized() {
72 let output: OsWindows = serde_json::from_str(LS_OUTPUT_JSON).unwrap();
73
74 let expected: &OsWindows = &LS_OUTPUT;
75
76 assert_eq!(&output, expected);
77 }
78}
79
80#[cfg(test)]
81pub mod test_fixture {
82 use std::path::PathBuf;
83
84 use lazy_static::lazy_static;
85
86 use super::{OsWindow, OsWindowId, OsWindows, Process, Tab, TabId, Window, WindowId};
87
88 lazy_static! {
89 pub static ref LS_OUTPUT: OsWindows = OsWindows(
90 vec![
91 OsWindow {
92 id: OsWindowId(1u32),
93 is_active: true,
94 is_focused: true,
95 tabs: vec![
96 Tab {
97 id: TabId(1u32),
98 is_active: true,
99 is_focused: true,
100 windows: vec![
101 Window {
102 id: WindowId(1u32),
103 is_active: false,
104 is_focused: false,
105 foreground_processes: vec![
106 Process {
107 cmdline: vec![
108 "/nix/store/6z1v4fzjw416c38j4013y9wam07q5zbs-rust-default-1.73.0/libexec/rust-analyzer-proc-macro-srv".to_string()
109 ],
110 cwd: Some(PathBuf::from("/path/to/felis")),
111 pid: 40339
112 },
113 Process {
114 cmdline: vec![
115 "/nix/store/0g95h72qqdxlig31n6ahcz1ch1jsg9q4-rust-analyzer-unwrapped-2023-05-15/bin/rust-analyzer".to_string()
116 ],
117 cwd: None,
118 pid: 38646
119 },
120 Process {
121 cmdline: vec![
122 "/etc/profiles/per-user/gaborpihaj/bin/hx".to_string()
123 ],
124 cwd: Some(PathBuf::from("/path/to/felis")),
125 pid: 38411
126 }],
127 },
128 Window {
129 id: WindowId(2u32),
130 is_active: true,
131 is_focused: true,
132 foreground_processes: vec![
133 Process {
134 pid: 49915,
135 cwd: Some(PathBuf::from("/path/to/felis")),
136 cmdline: vec![
137 "kitten".to_string(),
138 "@".to_string(),
139 "ls".to_string(),
140 ],
141 },
142 ]
143 },
144 Window {
145 id: WindowId(3u32),
146 is_active: false,
147 is_focused: false,
148 foreground_processes: vec![
149 Process {
150 pid: 983,
151 cwd: Some(PathBuf::from("/path/to/felis")),
152 cmdline: vec![
153 "-zsh".to_string(),
154 ],
155 },
156 ],
157 }
158 ],
159 }
160 ],
161 }
162 ]
163 );
164 }
165
166 pub static LS_OUTPUT_JSON: &str = r#"[
167{
168 "id": 1,
169 "is_active": true,
170 "is_focused": true,
171 "last_focused": true,
172 "platform_window_id": 130,
173 "tabs": [
174 {
175 "active_window_history": [
176 3,
177 2,
178 1
179 ],
180 "enabled_layouts": [
181 "fat",
182 "grid",
183 "horizontal",
184 "splits",
185 "stack",
186 "tall",
187 "vertical"
188 ],
189 "id": 1,
190 "is_active": true,
191 "is_focused": true,
192 "layout": "grid",
193 "layout_opts": {},
194 "layout_state": {
195 "biased_cols": {},
196 "biased_rows": {}
197 },
198 "title": "kitty @ ls",
199 "windows": [
200 {
201 "cmdline": [
202 "-zsh"
203 ],
204 "columns": 119,
205 "cwd": "/path/to/felis",
206 "env": {},
207 "foreground_processes": [
208 {
209 "cmdline": [
210 "/nix/store/6z1v4fzjw416c38j4013y9wam07q5zbs-rust-default-1.73.0/libexec/rust-analyzer-proc-macro-srv"
211 ],
212 "cwd": "/path/to/felis",
213 "pid": 40339
214 },
215 {
216 "cmdline": [
217 "/nix/store/0g95h72qqdxlig31n6ahcz1ch1jsg9q4-rust-analyzer-unwrapped-2023-05-15/bin/rust-analyzer"
218 ],
219 "cwd": null,
220 "pid": 38646
221 },
222 {
223 "cmdline": [
224 "/etc/profiles/per-user/gaborpihaj/bin/hx"
225 ],
226 "cwd": "/path/to/felis",
227 "pid": 38411
228 }
229 ],
230 "id": 1,
231 "is_active": false,
232 "is_focused": false,
233 "is_self": false,
234 "lines": 47,
235 "pid": 863,
236 "title": "hx"
237 },
238 {
239 "cmdline": [
240 "-zsh"
241 ],
242 "columns": 119,
243 "cwd": "/path/to/felis",
244 "env": {},
245 "foreground_processes": [
246 {
247 "cmdline": [
248 "kitten",
249 "@",
250 "ls"
251 ],
252 "cwd": "/path/to/felis",
253 "pid": 49915
254 }
255 ],
256 "id": 2,
257 "is_active": true,
258 "is_focused": true,
259 "is_self": true,
260 "lines": 23,
261 "pid": 972,
262 "title": "kitty @ ls"
263 },
264 {
265 "cmdline": [
266 "-zsh"
267 ],
268 "columns": 119,
269 "cwd": "/path/to/felis",
270 "env": {},
271 "foreground_processes": [
272 {
273 "cmdline": [
274 "-zsh"
275 ],
276 "cwd": "/path/to/felis",
277 "pid": 983
278 }
279 ],
280 "id": 3,
281 "is_active": false,
282 "is_focused": false,
283 "is_self": false,
284 "lines": 24,
285 "pid": 983,
286 "title": "/path/to/felis"
287 }
288 ]
289 }
290 ],
291 "wm_class": "kitty",
292 "wm_name": "kitty"
293 }
294]"#;
295}