Add positional-arguments
setting (#804)
Allow recipe arguments to be passed as positional arguments to commands.
This commit is contained in:
parent
d03aedd5c4
commit
67bd318bf9
@ -60,6 +60,7 @@ export : 'export' assignment
|
||||
|
||||
setting : 'set' 'dotenv-load' boolean?
|
||||
| 'set' 'export' boolean?
|
||||
| 'set' 'positional-arguments' boolean?
|
||||
| 'set' 'shell' ':=' '[' string (',' string)* ','? ']'
|
||||
|
||||
boolean : ':=' ('true' | 'false')
|
||||
|
29
README.adoc
29
README.adoc
@ -388,9 +388,10 @@ foo:
|
||||
[options="header"]
|
||||
|=================
|
||||
| Name | Value | Description
|
||||
| `dotenv-load` | `true` or `false` | Load a `.env` file, if present.
|
||||
| `export` | `true` or `false` | Export all variables as environment variables.
|
||||
|`shell` | `[COMMAND, ARGS...]` | Set the command used to invoke recipes and evaluate backticks.
|
||||
| `dotenv-load` | boolean | Load a `.env` file, if present.
|
||||
| `export` | boolean | Export all variables as environment variables.
|
||||
| `positional-arguments` | boolean | Pass positional arguments.
|
||||
| `shell` | `[COMMAND, ARGS...]` | Set the command used to invoke recipes and evaluate backticks.
|
||||
|=================
|
||||
|
||||
Boolean settings can be written as:
|
||||
@ -429,6 +430,28 @@ hello
|
||||
goodbye
|
||||
```
|
||||
|
||||
==== Positional Arguments
|
||||
|
||||
If `positional-arguments` is `true`, recipe arguments will be passed as positional arguments to commands. For linewise recipes, argument `$0` will be the name of the Recipe.
|
||||
|
||||
For example, running this recipe:
|
||||
|
||||
```make
|
||||
set positional-arguments
|
||||
|
||||
@foo bar:
|
||||
echo $0
|
||||
echo $1
|
||||
```
|
||||
|
||||
Will produce the following output:
|
||||
|
||||
```
|
||||
$ just foo hello
|
||||
foo
|
||||
hello
|
||||
```
|
||||
|
||||
==== Shell
|
||||
|
||||
The `shell` setting controls the command used to invoke recipe lines and backticks. Shebang recipes are unaffected.
|
||||
|
@ -71,6 +71,9 @@ impl<'src> Analyzer<'src> {
|
||||
Setting::Export(export) => {
|
||||
settings.export = export;
|
||||
},
|
||||
Setting::PositionalArguments(positional_arguments) => {
|
||||
settings.positional_arguments = positional_arguments;
|
||||
},
|
||||
Setting::Shell(shell) => {
|
||||
assert!(settings.shell.is_none());
|
||||
settings.shell = Some(shell);
|
||||
|
@ -266,7 +266,7 @@ impl<'src> Justfile<'src> {
|
||||
}
|
||||
}
|
||||
|
||||
recipe.run(context, dotenv, scope.child(), search)?;
|
||||
recipe.run(context, dotenv, scope.child(), search, arguments)?;
|
||||
|
||||
let mut invocation = vec![recipe.name().to_owned()];
|
||||
for argument in arguments.iter().cloned() {
|
||||
|
@ -4,14 +4,15 @@ use crate::common::*;
|
||||
#[strum(serialize_all = "kebab_case")]
|
||||
pub(crate) enum Keyword {
|
||||
Alias,
|
||||
DotenvLoad,
|
||||
Else,
|
||||
Export,
|
||||
DotenvLoad,
|
||||
True,
|
||||
False,
|
||||
If,
|
||||
PositionalArguments,
|
||||
Set,
|
||||
Shell,
|
||||
True,
|
||||
}
|
||||
|
||||
impl Keyword {
|
||||
|
@ -191,7 +191,8 @@ impl<'src> Node<'src> for Set<'src> {
|
||||
|
||||
use Setting::*;
|
||||
match &self.value {
|
||||
DotenvLoad(value) | Export(value) => set.push_mut(value.to_string()),
|
||||
DotenvLoad(value) | Export(value) | PositionalArguments(value) =>
|
||||
set.push_mut(value.to_string()),
|
||||
Shell(setting::Shell { command, arguments }) => {
|
||||
set.push_mut(Tree::string(&command.cooked));
|
||||
for argument in arguments {
|
||||
|
@ -717,6 +717,12 @@ impl<'tokens, 'src> Parser<'tokens, 'src> {
|
||||
value: Setting::Export(value),
|
||||
name,
|
||||
});
|
||||
} else if Keyword::PositionalArguments == lexeme {
|
||||
let value = self.parse_set_bool()?;
|
||||
return Ok(Set {
|
||||
value: Setting::PositionalArguments(value),
|
||||
name,
|
||||
});
|
||||
}
|
||||
|
||||
self.expect(ColonEquals)?;
|
||||
@ -1678,6 +1684,24 @@ mod tests {
|
||||
tree: (justfile (set dotenv_load false)),
|
||||
}
|
||||
|
||||
test! {
|
||||
name: set_positional_arguments_implicit,
|
||||
text: "set positional-arguments",
|
||||
tree: (justfile (set positional_arguments true)),
|
||||
}
|
||||
|
||||
test! {
|
||||
name: set_positional_arguments_true,
|
||||
text: "set positional-arguments := true",
|
||||
tree: (justfile (set positional_arguments true)),
|
||||
}
|
||||
|
||||
test! {
|
||||
name: set_positional_arguments_false,
|
||||
text: "set positional-arguments := false",
|
||||
tree: (justfile (set positional_arguments false)),
|
||||
}
|
||||
|
||||
test! {
|
||||
name: set_shell_no_arguments,
|
||||
text: "set shell := ['tclsh']",
|
||||
|
@ -74,6 +74,7 @@ impl<'src, D> Recipe<'src, D> {
|
||||
dotenv: &BTreeMap<String, String>,
|
||||
scope: Scope<'src, 'run>,
|
||||
search: &'run Search,
|
||||
arguments: &[&'run str],
|
||||
) -> RunResult<'src, ()> {
|
||||
let config = &context.config;
|
||||
|
||||
@ -176,6 +177,10 @@ impl<'src, D> Recipe<'src, D> {
|
||||
output_error,
|
||||
})?;
|
||||
|
||||
if context.settings.positional_arguments {
|
||||
command.args(arguments);
|
||||
}
|
||||
|
||||
command.export(context.settings, dotenv, &scope);
|
||||
|
||||
// run it!
|
||||
@ -260,6 +265,11 @@ impl<'src, D> Recipe<'src, D> {
|
||||
|
||||
cmd.arg(command);
|
||||
|
||||
if context.settings.positional_arguments {
|
||||
cmd.arg(self.name.lexeme());
|
||||
cmd.args(arguments);
|
||||
}
|
||||
|
||||
if config.verbosity.quiet() {
|
||||
cmd.stderr(Stdio::null());
|
||||
cmd.stdout(Stdio::null());
|
||||
|
@ -2,9 +2,10 @@ use crate::common::*;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) enum Setting<'src> {
|
||||
Shell(Shell<'src>),
|
||||
Export(bool),
|
||||
DotenvLoad(bool),
|
||||
Export(bool),
|
||||
PositionalArguments(bool),
|
||||
Shell(Shell<'src>),
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
|
@ -2,17 +2,19 @@ use crate::common::*;
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub(crate) struct Settings<'src> {
|
||||
pub(crate) dotenv_load: Option<bool>,
|
||||
pub(crate) export: bool,
|
||||
pub(crate) shell: Option<setting::Shell<'src>>,
|
||||
pub(crate) dotenv_load: Option<bool>,
|
||||
pub(crate) export: bool,
|
||||
pub(crate) positional_arguments: bool,
|
||||
pub(crate) shell: Option<setting::Shell<'src>>,
|
||||
}
|
||||
|
||||
impl<'src> Settings<'src> {
|
||||
pub(crate) fn new() -> Settings<'src> {
|
||||
Settings {
|
||||
dotenv_load: None,
|
||||
export: false,
|
||||
shell: None,
|
||||
dotenv_load: None,
|
||||
export: false,
|
||||
positional_arguments: false,
|
||||
shell: None,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -17,6 +17,7 @@ mod init;
|
||||
mod interrupts;
|
||||
mod invocation_directory;
|
||||
mod misc;
|
||||
mod positional_arguments;
|
||||
mod quiet;
|
||||
mod readme;
|
||||
mod search;
|
||||
|
66
tests/positional_arguments.rs
Normal file
66
tests/positional_arguments.rs
Normal file
@ -0,0 +1,66 @@
|
||||
test! {
|
||||
name: linewise,
|
||||
justfile: r#"
|
||||
set positional-arguments
|
||||
|
||||
foo bar baz:
|
||||
echo $0
|
||||
echo $1
|
||||
echo $2
|
||||
echo "$@"
|
||||
"#,
|
||||
args: ("foo", "hello", "goodbye"),
|
||||
stdout: "
|
||||
foo
|
||||
hello
|
||||
goodbye
|
||||
hello goodbye
|
||||
",
|
||||
stderr: r#"
|
||||
echo $0
|
||||
echo $1
|
||||
echo $2
|
||||
echo "$@"
|
||||
"#,
|
||||
}
|
||||
|
||||
test! {
|
||||
name: variadic_linewise,
|
||||
justfile: r#"
|
||||
set positional-arguments
|
||||
|
||||
foo *bar:
|
||||
echo $1
|
||||
echo "$@"
|
||||
"#,
|
||||
args: ("foo", "a", "b", "c"),
|
||||
stdout: "a\na b c\n",
|
||||
stderr: "echo $1\necho \"$@\"\n",
|
||||
}
|
||||
|
||||
test! {
|
||||
name: shebang,
|
||||
justfile: "
|
||||
set positional-arguments
|
||||
|
||||
foo bar:
|
||||
#!/bin/sh
|
||||
echo $1
|
||||
",
|
||||
args: ("foo", "hello"),
|
||||
stdout: "hello\n",
|
||||
}
|
||||
|
||||
test! {
|
||||
name: variadic_shebang,
|
||||
justfile: r#"
|
||||
set positional-arguments
|
||||
|
||||
foo *bar:
|
||||
#!/bin/sh
|
||||
echo $1
|
||||
echo "$@"
|
||||
"#,
|
||||
args: ("foo", "a", "b", "c"),
|
||||
stdout: "a\na b c\n",
|
||||
}
|
Loading…
Reference in New Issue
Block a user