1#![deny(unsafe_op_in_unsafe_fn)]
2#![deny(clippy::all)]
3
4#[cfg(target_os = "macos")]
5#[macro_use]
6extern crate objc2;
7
8#[cfg(target_os = "macos")]
9#[macro_use]
10extern crate core;
11
12mod common;
13
14#[cfg(target_os = "windows")]
15mod win32;
16
17#[cfg(target_os = "linux")]
18mod linux;
19
20#[cfg(target_os = "macos")]
21mod macos;
22
23#[cfg(target_os = "windows")]
24use win32::init_platform_api;
25
26#[cfg(target_os = "linux")]
27use linux::init_platform_api;
28
29#[cfg(target_os = "macos")]
30use macos::init_platform_api;
31
32#[cfg(all(feature = "macos_permission", target_os = "macos"))]
33pub use macos::permission;
37
38pub use common::{
39 api::{empty_entity, os_name, Api},
40 result::Result,
41 x_win_struct::{
42 icon_info::IconInfo, process_info::ProcessInfo, usage_info::UsageInfo, window_info::WindowInfo,
43 window_position::WindowPosition,
44 },
45};
46
47pub fn get_window_icon(window_info: &WindowInfo) -> Result<IconInfo> {
52 let api = init_platform_api();
53 let icon_info = api.get_app_icon(window_info)?;
54 Ok(icon_info)
55}
56
57pub fn get_browser_url(window_info: &WindowInfo) -> Result<String> {
62 let api = init_platform_api();
63 let browser_url = api.get_browser_url(window_info)?;
64 Ok(browser_url)
65}
66
67pub fn get_active_window() -> Result<WindowInfo> {
72 let api = init_platform_api();
73 let active_window = api.get_active_window()?;
74 Ok(active_window)
75}
76
77pub fn get_open_windows() -> Result<Vec<WindowInfo>> {
82 let api = init_platform_api();
83 let open_windows = api.get_open_windows()?;
84 Ok(open_windows)
85}
86
87pub fn install_extension() -> Result<bool> {
93 #[cfg(not(target_os = "linux"))]
94 {
95 Ok(false)
96 }
97 #[cfg(target_os = "linux")]
98 {
99 linux::gnome_install_extension()
100 }
101}
102
103pub fn uninstall_extension() -> Result<bool> {
109 #[cfg(not(target_os = "linux"))]
110 {
111 Ok(false)
112 }
113 #[cfg(target_os = "linux")]
114 {
115 linux::gnome_uninstall_extension()
116 }
117}
118
119pub fn enable_extension() -> Result<bool> {
124 #[cfg(not(target_os = "linux"))]
125 {
126 Ok(false)
127 }
128 #[cfg(target_os = "linux")]
129 {
130 linux::gnome_enable_extension()
131 }
132}
133
134pub fn disable_extension() -> Result<bool> {
139 #[cfg(not(target_os = "linux"))]
140 {
141 Ok(false)
142 }
143 #[cfg(target_os = "linux")]
144 {
145 linux::gnome_disable_extension()
146 }
147}
148
149pub fn is_enabled_extension() -> Result<bool> {
154 #[cfg(not(target_os = "linux"))]
155 {
156 Ok(false)
157 }
158 #[cfg(target_os = "linux")]
159 {
160 linux::gnome_is_enabled_extension()
161 }
162}
163
164pub fn is_installed_extension() -> Result<bool> {
169 #[cfg(not(target_os = "linux"))]
170 {
171 Ok(false)
172 }
173 #[cfg(target_os = "linux")]
174 {
175 linux::gnome_is_installed_extension()
176 }
177}
178
179#[cfg(test)]
180mod tests {
181 use super::*;
182 #[cfg(not(target_os = "linux"))]
183 use std::process::Command;
184 #[cfg(not(target_os = "linux"))]
185 use std::{thread, time};
186
187 #[cfg(not(target_os = "linux"))]
188 struct TestContext;
189
190 #[cfg(not(target_os = "linux"))]
191 impl TestContext {
192 fn setup() -> Self {
193 let output = if cfg!(target_os = "windows") {
194 Command::new("cmd")
195 .args([
196 "/C",
197 "start",
198 "microsoft-edge:https://github.com",
199 "--no-first-run",
200 "--restore-last-session",
201 ])
202 .output()
203 .expect("failed to execute process")
204 } else {
205 Command::new("open")
206 .args(["-a", "Safari", "https://github.com"])
207 .output()
208 .expect("failed to execute process")
209 };
210 println!(
211 "[START] Command Status: {:?}; Command stdout: {:?}; Command stderr: {:?}",
212 output.status,
213 std::str::from_utf8(&output.stdout).unwrap_or("Error when convert output"),
214 std::str::from_utf8(&output.stderr).unwrap_or("Error when convert stderr")
215 );
216 thread::sleep(time::Duration::from_secs(3));
217 TestContext
218 }
219 }
220
221 #[cfg(not(target_os = "linux"))]
222 impl Drop for TestContext {
223 fn drop(&mut self) {
224 let output = if cfg!(target_os = "windows") {
225 Command::new("cmd")
226 .args(["/C", "taskkill", "/f", "/im", "msedge.exe"])
227 .output()
228 .expect("failed to execute process")
229 } else {
230 Command::new("killall")
231 .args(["Safari"])
232 .output()
233 .expect("failed to execute process")
234 };
235 println!(
236 "[DONE] Command Status: {:?}; Command stdout: {:?}; Command stderr: {:?}",
237 output.status,
238 std::str::from_utf8(&output.stdout).unwrap_or("Error when convert output"),
239 std::str::from_utf8(&output.stderr).unwrap_or("Error when convert stderr")
240 );
241 thread::sleep(time::Duration::from_secs(3));
242 }
243 }
244
245 fn test_osname() -> String {
246 #[cfg(target_os = "linux")]
247 {
248 r#"linux"#.to_owned()
249 }
250 #[cfg(target_os = "macos")]
251 {
252 r#"darwin"#.to_owned()
253 }
254 #[cfg(target_os = "windows")]
255 {
256 r#"win32"#.to_owned()
257 }
258 }
259
260 fn test_struct(window_info: WindowInfo) -> Result<()> {
261 assert_ne!(window_info.id, 0);
262 assert_ne!(window_info.title, String::from(""));
263 #[cfg(target_os = "linux")]
264 assert_eq!(window_info.os, r#"linux"#);
265 #[cfg(target_os = "macos")]
266 assert_eq!(window_info.os, r#"darwin"#);
267 #[cfg(target_os = "windows")]
268 assert_eq!(window_info.os, r#"win32"#);
269 Ok(())
270 }
271
272 #[test]
273 fn test_get_active_window() -> Result<()> {
274 match get_active_window() {
275 Ok(window_info) => test_struct(window_info),
276 Err(err) => Err(err),
277 }
278 }
279
280 #[test]
281 fn test_get_open_windows() -> Result<()> {
282 match get_open_windows() {
283 Ok(open_windows) => {
284 assert_ne!(open_windows.len(), 0);
285
286 if let Some(window_info) = open_windows.first() {
287 test_struct(window_info.to_owned())
288 } else {
289 Err("No open window".into())
290 }
291 }
292 Err(err) => Err(err),
293 }
294 }
295
296 #[test]
297 fn test_os_name() -> Result<()> {
298 let os_name = os_name();
299 assert_eq!(os_name, test_osname());
300 Ok(())
301 }
302
303 #[test]
304 fn test_empty_entity() -> Result<()> {
305 let window_info = empty_entity();
306 assert_eq!(window_info.id, 0);
307 assert_eq!(window_info.title, String::from(""));
308 assert_eq!(window_info.os, test_osname());
309 Ok(())
310 }
311
312 #[test]
313 fn test_get_window_icon_from_active_window() -> Result<()> {
314 match get_active_window() {
315 Ok(window_info) => match get_window_icon(&window_info) {
316 Ok(icon_info) => {
317 assert_ne!(icon_info.data, "");
318 assert_ne!(icon_info.height, 0);
319 assert_ne!(icon_info.width, 0);
320 Ok(())
321 }
322 Err(err) => Err(err),
323 },
324 Err(err) => Err(err),
325 }
326 }
327
328 #[test]
329 fn test_get_window_icon_from_open_windows() -> Result<()> {
330 match get_open_windows() {
331 Ok(open_windows) => match open_windows.first() {
332 Some(window_info) => match get_window_icon(&window_info) {
333 Ok(icon_info) => {
334 assert_ne!(icon_info.data, "");
335 assert_ne!(icon_info.height, 0);
336 assert_ne!(icon_info.width, 0);
337 Ok(())
338 }
339 Err(err) => Err(err),
340 },
341 None => Err("No open window".into()),
342 },
343 Err(err) => Err(err),
344 }
345 }
346
347 #[cfg(not(target_os = "linux"))]
348 #[test]
349 #[ignore = "Not working on ci/cd"]
350 fn test_get_browser_url_from_active_window() -> Result<()> {
351 #[allow(unused)]
352 let _context: TestContext = TestContext::setup();
353 match get_active_window() {
354 Ok(window_info) => match get_browser_url(&window_info) {
355 Ok(url) => {
356 assert!(url.starts_with("http"));
357 Ok(())
358 }
359 Err(err) => Err(err),
360 },
361 Err(err) => Err(err),
362 }
363 }
364
365 #[cfg(not(target_os = "linux"))]
366 #[test]
367 #[ignore = "Not working on ci/cd"]
368 fn test_get_browser_url_from_open_windows() -> Result<()> {
369 #[allow(unused)]
370 let _context = TestContext::setup();
371 match get_open_windows() {
372 Ok(open_windows) => {
373 assert_ne!(open_windows.len(), 0);
374 match open_windows.first() {
375 Some(window_info) => match get_browser_url(window_info) {
376 Ok(url) => {
377 assert!(url.starts_with("http"));
378 Ok(())
379 }
380 Err(err) => Err(err),
381 },
382 None => Err("No open window".into()),
383 }
384 }
385 Err(err) => Err(err),
386 }
387 }
388
389 #[cfg(target_os = "linux")]
390 #[test]
391 #[ignore = "Not working on ci/cd"]
392 fn test_get_browser_url_from_active_window() -> Result<()> {
393 #[allow(unused)]
394 match get_active_window() {
395 Ok(window_info) => match get_browser_url(&window_info) {
396 Ok(url) => {
397 assert!(url.eq("URL recovery not supported on Linux distribution!"));
398 Ok(())
399 }
400 Err(err) => Err(err),
401 },
402 Err(err) => Err(err),
403 }
404 }
405
406 #[cfg(target_os = "linux")]
407 #[test]
408 #[ignore = "Not working on ci/cd"]
409 fn test_get_browser_url_from_open_windows() -> Result<()> {
410 #[allow(unused)]
411 match get_open_windows() {
412 Ok(open_windows) => {
413 assert_ne!(open_windows.len(), 0);
414 match open_windows.first() {
415 Some(window_info) => match get_browser_url(window_info) {
416 Ok(url) => {
417 assert!(url.eq("URL recovery not supported on Linux distribution!"));
418 Ok(())
419 }
420 Err(err) => Err(err),
421 },
422 None => Err("No open window".into()),
423 }
424 }
425 Err(err) => Err(err),
426 }
427 }
428
429 #[cfg(all(feature = "macos_permission", target_os = "macos"))]
430 #[test]
431 #[ignore = "Not working on ci/cd"]
432 fn test_check_screen_record_permission() -> Result<()> {
433 use macos::permission::check_screen_record_permission;
434 let value = check_screen_record_permission();
435 assert_eq!(value, true);
436 Ok(())
437 }
438
439 #[cfg(all(feature = "macos_permission", target_os = "macos"))]
440 #[test]
441 #[ignore = "Not working on ci/cd"]
442 fn test_request_screen_record_permission() -> Result<()> {
443 use macos::permission::request_screen_record_permission;
444 let value = request_screen_record_permission();
445 assert_eq!(value, true);
446 Ok(())
447 }
448
449 #[cfg(target_os = "linux")]
450 #[test]
451 #[ignore = "Not working on ci/cd"]
452 fn test_install_extension() -> Result<()> {
453 let value = install_extension()?;
454 assert_eq!(value, true);
455 Ok(())
456 }
457
458 #[cfg(target_os = "linux")]
459 #[test]
460 #[ignore = "Not working on ci/cd"]
461 fn test_uninstall_extension() -> Result<()> {
462 let value = uninstall_extension()?;
463 assert_eq!(value, true);
464 Ok(())
465 }
466
467 #[cfg(target_os = "linux")]
468 #[test]
469 #[ignore = "Not working on ci/cd"]
470 fn test_enable_extension() -> Result<()> {
471 let value = enable_extension()?;
472 assert_eq!(value, true);
473 Ok(())
474 }
475
476 #[cfg(target_os = "linux")]
477 #[test]
478 #[ignore = "Not working on ci/cd"]
479 fn test_disable_extension() -> Result<()> {
480 let value = disable_extension()?;
481 assert_eq!(value, true);
482 Ok(())
483 }
484
485 #[cfg(target_os = "linux")]
486 #[test]
487 #[ignore = "Not working on ci/cd"]
488 fn test_is_enabled_extension() -> Result<()> {
489 let value = is_enabled_extension()?;
490 assert_eq!(value, true);
491 Ok(())
492 }
493
494 #[cfg(target_os = "linux")]
495 #[test]
496 #[ignore = "Not working on ci/cd"]
497 fn test_is_installed_extension() -> Result<()> {
498 let value = is_installed_extension()?;
499 assert_eq!(value, true);
500 Ok(())
501 }
502}