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?
|
setting : 'set' 'dotenv-load' boolean?
|
||||||
| 'set' 'export' boolean?
|
| 'set' 'export' boolean?
|
||||||
|
| 'set' 'positional-arguments' boolean?
|
||||||
| 'set' 'shell' ':=' '[' string (',' string)* ','? ']'
|
| 'set' 'shell' ':=' '[' string (',' string)* ','? ']'
|
||||||
|
|
||||||
boolean : ':=' ('true' | 'false')
|
boolean : ':=' ('true' | 'false')
|
||||||
|
29
README.adoc
29
README.adoc
@ -388,9 +388,10 @@ foo:
|
|||||||
[options="header"]
|
[options="header"]
|
||||||
|=================
|
|=================
|
||||||
| Name | Value | Description
|
| Name | Value | Description
|
||||||
| `dotenv-load` | `true` or `false` | Load a `.env` file, if present.
|
| `dotenv-load` | boolean | Load a `.env` file, if present.
|
||||||
| `export` | `true` or `false` | Export all variables as environment variables.
|
| `export` | boolean | Export all variables as environment variables.
|
||||||
|`shell` | `[COMMAND, ARGS...]` | Set the command used to invoke recipes and evaluate backticks.
|
| `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:
|
Boolean settings can be written as:
|
||||||
@ -429,6 +430,28 @@ hello
|
|||||||
goodbye
|
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
|
==== Shell
|
||||||
|
|
||||||
The `shell` setting controls the command used to invoke recipe lines and backticks. Shebang recipes are unaffected.
|
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) => {
|
Setting::Export(export) => {
|
||||||
settings.export = export;
|
settings.export = export;
|
||||||
},
|
},
|
||||||
|
Setting::PositionalArguments(positional_arguments) => {
|
||||||
|
settings.positional_arguments = positional_arguments;
|
||||||
|
},
|
||||||
Setting::Shell(shell) => {
|
Setting::Shell(shell) => {
|
||||||
assert!(settings.shell.is_none());
|
assert!(settings.shell.is_none());
|
||||||
settings.shell = Some(shell);
|
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()];
|
let mut invocation = vec![recipe.name().to_owned()];
|
||||||
for argument in arguments.iter().cloned() {
|
for argument in arguments.iter().cloned() {
|
||||||
|
@ -4,14 +4,15 @@ use crate::common::*;
|
|||||||
#[strum(serialize_all = "kebab_case")]
|
#[strum(serialize_all = "kebab_case")]
|
||||||
pub(crate) enum Keyword {
|
pub(crate) enum Keyword {
|
||||||
Alias,
|
Alias,
|
||||||
|
DotenvLoad,
|
||||||
Else,
|
Else,
|
||||||
Export,
|
Export,
|
||||||
DotenvLoad,
|
|
||||||
True,
|
|
||||||
False,
|
False,
|
||||||
If,
|
If,
|
||||||
|
PositionalArguments,
|
||||||
Set,
|
Set,
|
||||||
Shell,
|
Shell,
|
||||||
|
True,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Keyword {
|
impl Keyword {
|
||||||
|
@ -191,7 +191,8 @@ impl<'src> Node<'src> for Set<'src> {
|
|||||||
|
|
||||||
use Setting::*;
|
use Setting::*;
|
||||||
match &self.value {
|
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 }) => {
|
Shell(setting::Shell { command, arguments }) => {
|
||||||
set.push_mut(Tree::string(&command.cooked));
|
set.push_mut(Tree::string(&command.cooked));
|
||||||
for argument in arguments {
|
for argument in arguments {
|
||||||
|
@ -717,6 +717,12 @@ impl<'tokens, 'src> Parser<'tokens, 'src> {
|
|||||||
value: Setting::Export(value),
|
value: Setting::Export(value),
|
||||||
name,
|
name,
|
||||||
});
|
});
|
||||||
|
} else if Keyword::PositionalArguments == lexeme {
|
||||||
|
let value = self.parse_set_bool()?;
|
||||||
|
return Ok(Set {
|
||||||
|
value: Setting::PositionalArguments(value),
|
||||||
|
name,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
self.expect(ColonEquals)?;
|
self.expect(ColonEquals)?;
|
||||||
@ -1678,6 +1684,24 @@ mod tests {
|
|||||||
tree: (justfile (set dotenv_load false)),
|
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! {
|
test! {
|
||||||
name: set_shell_no_arguments,
|
name: set_shell_no_arguments,
|
||||||
text: "set shell := ['tclsh']",
|
text: "set shell := ['tclsh']",
|
||||||
|
@ -74,6 +74,7 @@ impl<'src, D> Recipe<'src, D> {
|
|||||||
dotenv: &BTreeMap<String, String>,
|
dotenv: &BTreeMap<String, String>,
|
||||||
scope: Scope<'src, 'run>,
|
scope: Scope<'src, 'run>,
|
||||||
search: &'run Search,
|
search: &'run Search,
|
||||||
|
arguments: &[&'run str],
|
||||||
) -> RunResult<'src, ()> {
|
) -> RunResult<'src, ()> {
|
||||||
let config = &context.config;
|
let config = &context.config;
|
||||||
|
|
||||||
@ -176,6 +177,10 @@ impl<'src, D> Recipe<'src, D> {
|
|||||||
output_error,
|
output_error,
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
|
if context.settings.positional_arguments {
|
||||||
|
command.args(arguments);
|
||||||
|
}
|
||||||
|
|
||||||
command.export(context.settings, dotenv, &scope);
|
command.export(context.settings, dotenv, &scope);
|
||||||
|
|
||||||
// run it!
|
// run it!
|
||||||
@ -260,6 +265,11 @@ impl<'src, D> Recipe<'src, D> {
|
|||||||
|
|
||||||
cmd.arg(command);
|
cmd.arg(command);
|
||||||
|
|
||||||
|
if context.settings.positional_arguments {
|
||||||
|
cmd.arg(self.name.lexeme());
|
||||||
|
cmd.args(arguments);
|
||||||
|
}
|
||||||
|
|
||||||
if config.verbosity.quiet() {
|
if config.verbosity.quiet() {
|
||||||
cmd.stderr(Stdio::null());
|
cmd.stderr(Stdio::null());
|
||||||
cmd.stdout(Stdio::null());
|
cmd.stdout(Stdio::null());
|
||||||
|
@ -2,9 +2,10 @@ use crate::common::*;
|
|||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub(crate) enum Setting<'src> {
|
pub(crate) enum Setting<'src> {
|
||||||
Shell(Shell<'src>),
|
|
||||||
Export(bool),
|
|
||||||
DotenvLoad(bool),
|
DotenvLoad(bool),
|
||||||
|
Export(bool),
|
||||||
|
PositionalArguments(bool),
|
||||||
|
Shell(Shell<'src>),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq)]
|
#[derive(Debug, PartialEq)]
|
||||||
|
@ -2,17 +2,19 @@ use crate::common::*;
|
|||||||
|
|
||||||
#[derive(Debug, PartialEq)]
|
#[derive(Debug, PartialEq)]
|
||||||
pub(crate) struct Settings<'src> {
|
pub(crate) struct Settings<'src> {
|
||||||
pub(crate) dotenv_load: Option<bool>,
|
pub(crate) dotenv_load: Option<bool>,
|
||||||
pub(crate) export: bool,
|
pub(crate) export: bool,
|
||||||
pub(crate) shell: Option<setting::Shell<'src>>,
|
pub(crate) positional_arguments: bool,
|
||||||
|
pub(crate) shell: Option<setting::Shell<'src>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'src> Settings<'src> {
|
impl<'src> Settings<'src> {
|
||||||
pub(crate) fn new() -> Settings<'src> {
|
pub(crate) fn new() -> Settings<'src> {
|
||||||
Settings {
|
Settings {
|
||||||
dotenv_load: None,
|
dotenv_load: None,
|
||||||
export: false,
|
export: false,
|
||||||
shell: None,
|
positional_arguments: false,
|
||||||
|
shell: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -17,6 +17,7 @@ mod init;
|
|||||||
mod interrupts;
|
mod interrupts;
|
||||||
mod invocation_directory;
|
mod invocation_directory;
|
||||||
mod misc;
|
mod misc;
|
||||||
|
mod positional_arguments;
|
||||||
mod quiet;
|
mod quiet;
|
||||||
mod readme;
|
mod readme;
|
||||||
mod search;
|
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