Add [metadata] recipe attribute (#2794)
Some checks are pending
CI / lint (push) Waiting to run
CI / msrv (push) Waiting to run
CI / pages (push) Waiting to run
CI / test (macos-latest) (push) Waiting to run
CI / test (ubuntu-latest) (push) Waiting to run
CI / test (windows-latest) (push) Waiting to run

This commit is contained in:
Wiktor Kwapisiewicz 2025-07-04 23:55:18 +02:00 committed by GitHub
parent ec87c05d9e
commit bfa5a6d99a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 165 additions and 12 deletions

View file

@ -2093,12 +2093,13 @@ change their behavior.
| Name | Type | Description |
|------|------|-------------|
| `[confirm]`<sup>1.17.0</sup> | recipe | Require confirmation prior to executing recipe. |
| `[confirm('PROMPT')]`<sup>1.23.0</sup> | recipe | Require confirmation prior to executing recipe with a custom prompt. |
| `[doc('DOC')]`<sup>1.27.0</sup> | module, recipe | Set recipe or module's [documentation comment](#documentation-comments) to `DOC`. |
| `[extension('EXT')]`<sup>1.32.0</sup> | recipe | Set shebang recipe script's file extension to `EXT`. `EXT` should include a period if one is desired. |
| `[group('NAME')]`<sup>1.27.0</sup> | module, recipe | Put recipe or module in in [group](#groups) `NAME`. |
| `[confirm(PROMPT)]`<sup>1.23.0</sup> | recipe | Require confirmation prior to executing recipe with a custom prompt. |
| `[doc(DOC)]`<sup>1.27.0</sup> | module, recipe | Set recipe or module's [documentation comment](#documentation-comments) to `DOC`. |
| `[extension(EXT)]`<sup>1.32.0</sup> | recipe | Set shebang recipe script's file extension to `EXT`. `EXT` should include a period if one is desired. |
| `[group(NAME)]`<sup>1.27.0</sup> | module, recipe | Put recipe or module in in [group](#groups) `NAME`. |
| `[linux]`<sup>1.8.0</sup> | recipe | Enable recipe on Linux. |
| `[macos]`<sup>1.8.0</sup> | recipe | Enable recipe on MacOS. |
| `[metadata(METADATA)]`<sup>master</sup> | recipe | Attach `METADATA` to recipe. |
| `[no-cd]`<sup>1.9.0</sup> | recipe | Don't change directory before executing recipe. |
| `[no-exit-message]`<sup>1.7.0</sup> | recipe | Don't print an error message if recipe fails. |
| `[no-quiet]`<sup>1.23.0</sup> | recipe | Override globally quiet recipes and always echo out the recipe. |

View file

@ -16,6 +16,7 @@ pub(crate) enum Attribute<'src> {
Group(StringLiteral<'src>),
Linux,
Macos,
Metadata(Vec<StringLiteral<'src>>),
NoCd,
NoExitMessage,
NoQuiet,
@ -32,7 +33,6 @@ impl AttributeDiscriminant {
fn argument_range(self) -> RangeInclusive<usize> {
match self {
Self::Confirm | Self::Doc => 0..=1,
Self::Group | Self::Extension | Self::WorkingDirectory => 1..=1,
Self::ExitMessage
| Self::Linux
| Self::Macos
@ -44,6 +44,8 @@ impl AttributeDiscriminant {
| Self::Private
| Self::Unix
| Self::Windows => 0..=0,
Self::Extension | Self::Group | Self::WorkingDirectory => 1..=1,
Self::Metadata => 1..=usize::MAX,
Self::Script => 0..=usize::MAX,
}
}
@ -85,6 +87,7 @@ impl<'src> Attribute<'src> {
AttributeDiscriminant::Group => Self::Group(arguments.into_iter().next().unwrap()),
AttributeDiscriminant::Linux => Self::Linux,
AttributeDiscriminant::Macos => Self::Macos,
AttributeDiscriminant::Metadata => Self::Metadata(arguments),
AttributeDiscriminant::NoCd => Self::NoCd,
AttributeDiscriminant::NoExitMessage => Self::NoExitMessage,
AttributeDiscriminant::NoQuiet => Self::NoQuiet,
@ -115,7 +118,7 @@ impl<'src> Attribute<'src> {
}
pub(crate) fn repeatable(&self) -> bool {
matches!(self, Attribute::Group(_))
matches!(self, Attribute::Group(_) | Attribute::Metadata(_))
}
}
@ -124,12 +127,6 @@ impl Display for Attribute<'_> {
write!(f, "{}", self.name())?;
match self {
Self::Confirm(Some(argument))
| Self::Doc(Some(argument))
| Self::Extension(argument)
| Self::Group(argument)
| Self::WorkingDirectory(argument) => write!(f, "({argument})")?,
Self::Script(Some(shell)) => write!(f, "({shell})")?,
Self::Confirm(None)
| Self::Doc(None)
| Self::ExitMessage
@ -144,6 +141,22 @@ impl Display for Attribute<'_> {
| Self::Script(None)
| Self::Unix
| Self::Windows => {}
Self::Confirm(Some(argument))
| Self::Doc(Some(argument))
| Self::Extension(argument)
| Self::Group(argument)
| Self::WorkingDirectory(argument) => write!(f, "({argument})")?,
Self::Metadata(arguments) => {
write!(f, "(")?;
for (i, argument) in arguments.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "{argument}")?;
}
write!(f, ")")?;
}
Self::Script(Some(shell)) => write!(f, "({shell})")?,
}
Ok(())

View file

@ -131,6 +131,63 @@ fn unexpected_attribute_argument() {
.run();
}
#[test]
fn multiple_metadata_attributes() {
Test::new()
.justfile(
"
[metadata('example')]
[metadata('sample')]
[no-exit-message]
foo:
exit 1
",
)
.stderr("exit 1\n")
.status(1)
.run();
}
#[test]
fn multiple_metadata_attributes_with_multiple_args() {
Test::new()
.justfile(
"
[metadata('example', 'arg1')]
[metadata('sample', 'argument')]
[no-exit-message]
foo:
exit 1
",
)
.stderr("exit 1\n")
.status(1)
.run();
}
#[test]
fn expected_metadata_attribute_argument() {
Test::new()
.justfile(
"
[metadata]
foo:
exit 1
",
)
.stderr(
"
error: Attribute `metadata` got 0 arguments but takes at least 1 argument
justfile:1:2
1 [metadata]
^^^^^^^^
",
)
.status(1)
.run();
}
#[test]
fn doc_attribute() {
Test::new()

View file

@ -783,6 +783,88 @@ fn attribute() {
);
}
#[test]
fn single_metadata_attribute() {
case(
"
[metadata('example')]
foo:
",
Module {
first: Some("foo"),
recipes: [(
"foo",
Recipe {
attributes: [json!({"metadata": ["example"]})].into(),
name: "foo",
namepath: "foo",
..default()
},
)]
.into(),
..default()
},
);
}
#[test]
fn multiple_metadata_attributes() {
case(
"
[metadata('example')]
[metadata('sample')]
foo:
",
Module {
first: Some("foo"),
recipes: [(
"foo",
Recipe {
attributes: [
json!({"metadata": ["example"]}),
json!({"metadata": ["sample"]}),
]
.into(),
name: "foo",
namepath: "foo",
..default()
},
)]
.into(),
..default()
},
);
}
#[test]
fn multiple_metadata_attributes_with_multiple_arguments() {
case(
"
[metadata('example', 'arg1')]
[metadata('sample', 'argument')]
foo:
",
Module {
first: Some("foo"),
recipes: [(
"foo",
Recipe {
attributes: [
json!({"metadata": ["example", "arg1"]}),
json!({"metadata": ["sample", "argument"]}),
]
.into(),
name: "foo",
namepath: "foo",
..default()
},
)]
.into(),
..default()
},
);
}
#[test]
fn module() {
case_with_submodule(