simple_dmenu/
lib.rs

1#[macro_export]
2/// ## How to use
3///
4/// ### Standard
5/// Requires every argument to implement `.as_bytes()`.
6/// Might also not need to pass any arguments at all
7/// ```
8///
9/// // Example
10/// let output = dmenu!("1", "2", "3");
11/// ```
12///
13/// ---
14///
15/// ### Stringify
16/// Same as standard but passes every argument to `stringify!()`.
17/// Again, might also not need to pass any arguments at all
18/// ```
19///
20/// // Example
21/// let output = dmenu!(stringify 1, 2, 3);
22/// ```
23///
24/// ---
25///
26/// ### Iter
27/// Accepts an iterator.
28/// Every value yielded by the iterator needs to implement `.as_bytes()`
29/// ```
30///
31/// // Example
32/// let mut a = vec!["1", "2", "3"];
33/// a.push("4");
34///
35/// let output = dmenu!(iter a);
36/// ```
37///
38/// ---
39///
40/// ### Prompt
41/// Only shows a prompt with the specified prompt text
42/// ```
43///
44/// // Example
45/// let username = dmenu!(prompt "What's your name?");
46/// ```
47///
48/// ---
49///
50/// ### Optional arguments
51/// You can specify optional arguments to pass to dmenu by separating them with `; args`
52/// ```
53///
54/// // Example
55/// let output = dmenu!(stringify 1, 2, 3; args "-p", "Choose a number", "-l", "3");
56/// ```
57///
58/// ---
59///
60/// ### Arguments only
61/// Empty call, only allows to specify arguments to pass on to dmenu
62/// ```
63///
64/// // Example
65/// let output = dmenu!(args
66///     "-p", "What's your name?",
67///     "--nb", "#FFFFFF",
68///     "--nf", "#000000"
69/// );
70/// ```
71macro_rules! dmenu {
72    (__@internal_start@__ $(args $($arg:expr)+)?) => {{
73        let mut dmenu = ::std::process::Command::new("dmenu")
74            $(.args([$($arg),+]))?
75            .stdin(::std::process::Stdio::piped())
76            .stdout(::std::process::Stdio::piped())
77            .spawn()
78            .unwrap();
79
80        (
81            dmenu.stdin.take().unwrap(),
82            dmenu.stdout.take().unwrap()
83        )
84    }};
85
86    (__@internal_end@__ $stdin:expr, $stdout:expr) => {{
87        drop($stdin);
88
89        let mut buf = String::new();
90        ::std::io::Read::read_to_string(&mut $stdout, &mut buf).unwrap();
91        buf.trim().to_string()
92    }};
93
94    (iter $e:expr $(;args $($arg:expr),+)?) => {{
95        let (mut stdin, mut stdout) = dmenu!(__@internal_start@__ $(args $($arg)+)?);
96
97        for x in $e {
98            ::std::io::Write::write_all(&mut stdin, x.as_bytes()).unwrap();
99            ::std::io::Write::write_all(&mut stdin, b"\n").unwrap();
100        }
101
102        dmenu!(__@internal_end@__ stdin, stdout)
103    }};
104
105    (stringify $($e:expr),* $(;args $($arg:expr),+)?) => {{
106        let (mut stdin, mut stdout) = dmenu!(__@internal_start@__ $(args $($arg)+)?);
107
108        $(
109            ::std::io::Write::write_all(&mut stdin, stringify!($e).as_bytes()).unwrap();
110            ::std::io::Write::write_all(&mut stdin, b"\n").unwrap();
111        )*
112
113        dmenu!(__@internal_end@__ stdin, stdout)
114
115        // Copypasted because of unfixable recursion limit
116    }};
117
118    (args $($arg:expr),+) => {{
119        let (mut stdin, mut stdout) = dmenu!(__@internal_start@__ args $($arg)+);
120
121        dmenu!(__@internal_end@__ stdin, stdout)
122    }};
123
124    (prompt $text:expr $(;args $($arg:expr),+)?) => {{
125        let (mut stdin, mut stdout) = dmenu!(__@internal_start@__ args "-p" $text $($arg)*);
126
127        dmenu!(__@internal_end@__ stdin, stdout)
128    }};
129
130    ($($e:expr),* $(;args $($arg:expr),+)?) => {{
131        let (mut stdin, mut stdout) = dmenu!(__@internal_start@__ $(args $($arg)+)?);
132
133        $(
134            ::std::io::Write::write_all(&mut stdin, $e.as_bytes()).unwrap();
135            ::std::io::Write::write_all(&mut stdin, b"\n").unwrap();
136        )*
137
138        dmenu!(__@internal_end@__ stdin, stdout)
139    }};
140}