user_startup/utils/
mod.rs

1#[cfg(target_os = "linux")]
2mod linux;
3#[cfg(target_os = "macos")]
4mod macos;
5#[cfg(target_os = "windows")]
6mod windows;
7
8#[cfg(target_os = "linux")]
9pub use linux::*;
10#[cfg(target_os = "macos")]
11pub use macos::*;
12#[cfg(target_os = "windows")]
13pub use windows::*;
14
15pub trait IteratorExt: Iterator {
16    /// Split the iterator into two parts when the predicate first time turns
17    /// true.
18    ///
19    /// The first part is the part before the predicate is true.
20    /// The second part is the part after the predicate is true.
21    fn split_when(
22        self,
23        predicate: impl Fn(&Self::Item) -> bool,
24    ) -> (impl Iterator<Item = Self::Item>, Self)
25    where
26        Self: Sized;
27}
28
29impl<I: Iterator> IteratorExt for I {
30    fn split_when(
31        mut self,
32        predicate: impl Fn(&Self::Item) -> bool,
33    ) -> (impl Iterator<Item = Self::Item>, I)
34    where
35        Self: Sized,
36    {
37        let mut v = Vec::new();
38        for item in self.by_ref() {
39            if predicate(&item) {
40                break;
41            }
42            v.push(item);
43        }
44        (v.into_iter(), self)
45    }
46}
47
48/// Parse a command into an executable and arguments.
49///
50/// Note that the returned executable string will not contains leading and
51/// suffix quotes.
52pub fn parse_command(command: impl AsRef<str>) -> (String, String) {
53    let mut command = command.as_ref().trim().chars().peekable();
54    if ['\'', '"'].contains(command.peek().expect("command is empty")) {
55        let first_quote = command.next().unwrap();
56        let (executable, rest) = command.split_when(|c| c == &first_quote);
57        let args = rest.skip_while(|c| *c == ' ').collect::<String>();
58        (executable.collect::<String>(), args)
59    } else {
60        let (executable, args) = command.split_when(|c| *c == ' ');
61        (executable.collect::<String>(), args.collect::<String>())
62    }
63}
64
65#[cfg(test)]
66mod tests {
67    use super::*;
68
69    #[test]
70    fn test_split_when() {
71        let v = vec![1, 2, 3, 4, 5];
72        let (a, b) = v.into_iter().split_when(|x| *x == 3);
73        assert_eq!(a.collect::<Vec<_>>(), vec![1, 2]);
74        assert_eq!(b.collect::<Vec<_>>(), vec![4, 5]);
75    }
76
77    #[test]
78    fn test_parse_command() {
79        // Test with no quotes around the executable
80        let command = r#"ppp arg1 "'arg 2 with spaces'""#;
81        let (executable, args) = parse_command(command);
82        assert_eq!(executable, "ppp");
83        assert_eq!(args, "arg1 \"'arg 2 with spaces'\"");
84
85        // Test with single quotes around the executable
86        let command = r#"'C:\Program Files\My App\myapp.exe' arg1 'arg 2 with spaces'"#;
87        let (executable, args) = parse_command(command);
88        assert_eq!(executable, "C:\\Program Files\\My App\\myapp.exe");
89        assert_eq!(args, "arg1 'arg 2 with spaces'");
90
91        // Test with double quotes around the executable
92        let command = r#""C:\Program Files\My App\myapp.exe" arg1 'arg 2 with spaces'"#;
93        let (executable, args) = parse_command(command);
94        assert_eq!(executable, "C:\\Program Files\\My App\\myapp.exe");
95        assert_eq!(args, "arg1 'arg 2 with spaces'");
96    }
97}