hyprshell_core_lib/util/
exec.rs

1#[derive(Debug, Clone)]
2pub enum ExecType {
3    Flatpak(Box<str>, Box<str>),
4    PWA(Box<str>, Box<str>),
5    FlatpakPWA(Box<str>, Box<str>),
6    Absolute(Box<str>, Box<str>),
7    AppImage(Box<str>, Box<str>),
8    Relative(Box<str>),
9}
10
11const UNKNOWN_EXEC: &str = "unknown";
12
13#[must_use]
14pub fn analyse_exec(exec: &str) -> ExecType {
15    let exec_trim = exec.replace(['\'', '"'], "");
16    // pwa detection
17    if exec.contains("--app-id=") && exec.contains("--profile-directory=") {
18        // "flatpak 'run'" = pwa from browser inside flatpak
19        if exec.contains("flatpak run") || exec.contains("flatpak 'run'") {
20            let browser_exec_in_flatpak = exec_trim
21                .split_whitespace()
22                .find(|s| s.contains("--command="))
23                .and_then(|s| {
24                    s.split('=')
25                        .next_back()
26                        .and_then(|s| s.split('/').next_back())
27                })
28                .unwrap_or(UNKNOWN_EXEC);
29            let flatpak_identifier = exec_trim
30                .split_whitespace()
31                .skip(2)
32                .find(|arg| !arg.starts_with("--"))
33                .unwrap_or(UNKNOWN_EXEC);
34            ExecType::FlatpakPWA(
35                Box::from(flatpak_identifier),
36                Box::from(browser_exec_in_flatpak),
37            )
38        } else {
39            // normal PWA
40            let browser_exec = exec
41                .split_whitespace()
42                .next()
43                .and_then(|s| s.split('/').next_back())
44                .unwrap_or(UNKNOWN_EXEC);
45            let browser_full_exec = exec.split_whitespace().next().unwrap_or(UNKNOWN_EXEC);
46            ExecType::PWA(Box::from(browser_exec), Box::from(browser_full_exec))
47        }
48        // flatpak detection
49    } else if exec.contains("flatpak run") || exec.contains("flatpak 'run'") {
50        let command_in_flatpak = exec_trim
51            .split_whitespace()
52            .find(|s| s.contains("--command="))
53            .and_then(|s| {
54                s.split('=')
55                    .next_back()
56                    .and_then(|s| s.split('/').next_back())
57            })
58            .unwrap_or(UNKNOWN_EXEC);
59        let flatpak_identifier = exec_trim
60            .split_whitespace()
61            .skip(2)
62            .find(|arg| !arg.starts_with("--"))
63            .unwrap_or(UNKNOWN_EXEC);
64        ExecType::Flatpak(Box::from(flatpak_identifier), Box::from(command_in_flatpak))
65    } else if exec_trim.contains(".AppImage") {
66        // AppImage detection
67        let appimage_name = exec_trim
68            .split_whitespace()
69            .next()
70            .and_then(|s| s.split('/').next_back())
71            .and_then(|s| s.split('_').next())
72            .unwrap_or(UNKNOWN_EXEC);
73        ExecType::AppImage(Box::from(appimage_name), Box::from(exec))
74    } else if exec_trim.starts_with('/') {
75        let exec_name = exec_trim
76            .split_whitespace()
77            .next()
78            .and_then(|s| s.split('/').next_back())
79            .unwrap_or(UNKNOWN_EXEC);
80        ExecType::Absolute(Box::from(exec_name), Box::from(exec))
81    } else {
82        ExecType::Relative(Box::from(exec_trim))
83    }
84}
85
86#[cfg(test)]
87mod tests {
88    use super::*;
89
90    #[test_log::test]
91    #[test_log(default_log_filter = "trace")]
92    fn test_relative_exec() {
93        assert!(matches!(
94            analyse_exec("nautilus --new-window"),
95            ExecType::Relative(ref s) if &**s == "nautilus --new-window"
96        ));
97    }
98
99    #[test_log::test]
100    #[test_log(default_log_filter = "trace")]
101    fn test_flatpak_pwa_exec() {
102        assert!(matches!(
103            analyse_exec(
104                "flatpak 'run' '--command=/app/bin/chromium' 'org.chromium.Chromium' '--profile-directory=Default' '--app-id=awf'"
105            ),
106            ExecType::FlatpakPWA(ref id, ref browser) if &**id == "org.chromium.Chromium" && &**browser == "chromium"
107        ));
108    }
109
110    #[test_log::test]
111    #[test_log(default_log_filter = "trace")]
112    fn test_appimage_exec() {
113        assert!(matches!(
114            analyse_exec(
115                "/home/user/Applications/ungoogled-chromium_71.0.3578.98-2_linux_awf.AppImage %u"
116            ),
117            ExecType::AppImage(ref name, ref path) if &**name == "ungoogled-chromium" && &**path == "/home/user/Applications/ungoogled-chromium_71.0.3578.98-2_linux_awf.AppImage %u"
118        ));
119    }
120
121    #[test_log::test]
122    #[test_log(default_log_filter = "trace")]
123    fn test_absolute_pwa_exec() {
124        assert!(matches!(
125            analyse_exec(
126                "/opt/google/chrome/google-chrome --profile-directory=Default --app-id=awf"
127            ),
128            ExecType::PWA(ref browser, ref path) if &**browser == "google-chrome" && &**path == "/opt/google/chrome/google-chrome"
129        ));
130    }
131
132    #[test_log::test]
133    #[test_log(default_log_filter = "trace")]
134    fn test_flatpak_exec() {
135        assert!(matches!(
136            analyse_exec("flatpak run org.mozilla.firefox"),
137            ExecType::Flatpak(ref id, ref command) if &**id == "org.mozilla.firefox" && &**command == "unknown"
138        ));
139    }
140
141    #[test_log::test]
142    #[test_log(default_log_filter = "trace")]
143    fn test_absolute_exec() {
144        assert!(matches!(
145            analyse_exec("/usr/bin/firefox"),
146            ExecType::Absolute(ref name, ref path) if &**name == "firefox" && &**path == "/usr/bin/firefox"
147        ));
148    }
149}