command-macros 0.1.10

Macros for creating std::process::Command with shell-like syntax

Crate command_macros [] [src]

Macros for creating std::process::Command with shell-like syntax.

The command! macro is a syntax extension and requires nightly, the cmd! is simpler version built using macro_rules!.

This page describes syntax used by both command! and cmd! macros. See the github page for more general introduction.

Features marked with * are unavailable for cmd!.

Naked idents

First ident is treated as command name, the rest are parsed as arugments. This invocation:

cmd!(echo foo bar).status().unwrap();

expands to

{
    let cmd = ::std::process::Command::new("echo");
    cmd.arg("foo");
    cmd.arg("bar");
    cmd
}.status().unwrap()

(expression) (OsStr expression)

Interior of ( ) is parsed as Rust expression which should evaluate to T: AsRef<OsStr>. This will be put in cmd.arg(& $expr). The & is added automatically, like in println!, to prevent accidentally moving arguments.

let filename = String::from("foo bar");
let get_command = || "touch";
cmd!( (get_command()) (filename) ).status().unwrap();

((expression)) (ToString expression)

Interior of (( )) is parsed as Rust expression which should evaluate to T: ToString. Similar rules as with ( ) apply.

The following should echo 4

cmd!( echo ((2+2)) ).status().unwrap();

[expression] (args expression)

Interior of [ ] is parsed as Rust expression which should evaluate to [T: AsRef<OsStr>] (modulo Deref). This expression will be put in cmd.args(& $expr)

let args: Vec<_> = std::env::args_os().collect();
cmd!( (args[1]) [args[2..]] ).status().unwrap();

{expression} (Command expression)

Interior of { } is parsed as Rust expression which should evaluate to Command or &mut Command (or anything that has arg and args methods). It is allowed only at the beginning of macro. It is helpful when you want to append arguments to existing command:

let mut cmd = ::std::process::Command::new("echo");
cmd!( {&mut cmd} bar baz ).status().unwrap();

Strings*

String literals work like in shell – they expand to single argument or part of it. Character literals and raw string literals are also supported. Note that shell-style "$variables" won't work here.

command!("echo" "single argument" "***")

cmd! workaroud:

cmd!(echo ("single argument") ("***"))

Arbitrary tokens*

Everything that is not [block], {block}, (block) or string literal, will be stringified. This is mostly helpful for unix-like flags. Everything within a single whitespace-separated chunk will be treated as a single argument. In the following example we are passing three arguments to a foo.2.5 command.

command!(foo.2.5 --flag   -v:c -=|.|=-).status().unwrap();

cmd! workaround: ("--flag").

(-flags)

When your flag contains only -+=,.;: and idents, you can omit the quotes. The flag has to start with - or +.

This is only necessary for cmd!, when using command!, it will generate warning, as you can just remove the surrounding parens.

So instead

cmd!(foo ("--bar=baz"))

you can write

cmd!(foo (--bar=baz)

Please note that all whitespace will be ignored.

Multi-part arguments*

You can mix ((e)), (e), tokens and strings within a single arguments as long as they are not separated by whitespace. The following will touch the foo.v5.special edition file.

let p = Path::new("foo");
let version = 5;
command!(touch (p).v((version))".special edition")

This is roughly equivalent to (format!(...)), but the macro version can handle OsStrs (such as Path).

Please note that this is not supported by cmd!, which would evaluate every part as separate argument.

{}*

Empty {} is treated as "{}". This is handy when using commands like find. There has to be no space between braces.

If

The if token should be surrounded by whitespace. The expression (and pattern in if-let) is parsed as Rust, the inside of {block} is parsed using regular commmand! syntax and can evaluate to multiple arguments (or 0). The following should pass --number 5 to command foo.

let bar = 5;
let option = Some(5);
command!(foo
    if bar > 10 { zzz }
    else if let Some(a) = option { --number ((a)) }
).status().unwrap();

cmd! limitations: else if is not supported, expression has to be in parens.

cmd!(foo
    if (bar > 10) { zzz }
    else { if let Some(a) = (option) { ("--number") ((a)) } }
).status().unwrap();

Match

The match token should be surrounded by whitespace. The expression and patterns are parsed as Rust. On the right side of => there should be a {block}, which will be parsed (similarly to if blocks) using regular command! syntax.

This example will pass a single argument yes to foo command.

let option = Some(5);
command!(foo
    match option {
        Some(x) if x > 10 => {}
        _ => { yes }
    }
).status().unwrap()

cmd! limitation: expression after match has to be in parens.

For

The for token should be surrounded by whitespace. The expression and patterns are parsed as Rust. The interior of block is parsed using command! syntax, and will be evaluated in every iteration.

This example will pass three arguments 1 2 3.

command!(echo
    for x in 1..4 {
        ((x))
    }
).status().unwrap()

Macros

cmd

Simple macro for creating Command.