1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
#![forbid(unsafe_code)]
#![warn(rust_2018_idioms)]
use proc_macro::TokenStream;
use proc_macro2::TokenStream as TokenStream2;
use quote::quote;
use syn::parse::{Parse, ParseStream};
use syn::{parse_macro_input, punctuated::Punctuated, Token};
use vec1::Vec1;
struct Command {
program: syn::Expr,
args: Vec<syn::Expr>,
}
impl Parse for Command {
fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
let mut exprs =
Punctuated::<syn::Expr, Token![,]>::parse_separated_nonempty(input)?.into_iter();
Ok(Command {
program: exprs.next().unwrap(),
args: exprs.collect(),
})
}
}
struct Commands(Vec1<Command>);
impl Parse for Commands {
fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
let mut commands = Vec1::new(Command::parse(input)?);
while !input.is_empty() {
<Token![=>]>::parse(input)?;
commands.push(Command::parse(input)?);
}
Ok(Self(commands))
}
}
impl Commands {
fn into_token_stream(self) -> TokenStream2 {
let mut i = 0usize;
let ts = self.0.mapped_ref(|command| {
let program = &command.program;
let args = &command.args;
i += 1;
quote! {{
let mut cmd = ::std::process::Command::new(#program);
#(cmd.arg(#args);)*
cmd
}}
});
match ts.split_off_first() {
(first, rest) if rest.is_empty() => first,
(first, rest) => {
let ts = rest.into_iter().fold(first, |mut acc, x| {
acc.extend(quote! {,});
acc.extend(x);
acc
});
quote! { ::procmd::PipeCommand::new([#ts]) }
}
}
}
}
#[proc_macro]
pub fn cmd(input: TokenStream) -> TokenStream {
let commands = parse_macro_input!(input as Commands);
commands.into_token_stream().into()
}