Skip to content

Commit

Permalink
Reworked group commit
Browse files Browse the repository at this point in the history
  • Loading branch information
neunenak committed May 20, 2024
1 parent c6612de commit cd843c9
Show file tree
Hide file tree
Showing 12 changed files with 357 additions and 128 deletions.
62 changes: 62 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -1624,6 +1624,7 @@ Recipes may be annotated with attributes that change their behavior.
|------|-------------|
| `[confirm]`<sup>1.17.0</sup> | Require confirmation prior to executing recipe. |
| `[confirm("prompt")]`<sup>1.23.0</sup> | Require confirmation prior to executing recipe with a custom prompt. |
| `[group("group name")]`<sup>1.27.0</sup> | See [Recipe Groups](#recipe-groups)]|
| `[linux]`<sup>1.8.0</sup> | Enable recipe on Linux. |
| `[macos]`<sup>1.8.0</sup> | Enable recipe on MacOS. |
| `[no-cd]`<sup>1.9.0</sup> | Don't change directory before executing recipe. |
Expand Down Expand Up @@ -1722,6 +1723,67 @@ delete-everything:
rm -rf *
```

### Recipe Groups


Recipes can be optionally marked as belonging to one or more groups:

```just
[group("lint")]
js-lint:
echo "Runninng javascript linter"
[group("rust jobs")]
[group("lint")]
rust-lint:
echo "Runninng rust linter"
[group("lint")]
cpp-lint:
echo "Running C++ linter"
# Not in a group
email-everyone:
echo "Sending out mass email
```

`[group: lint]` is a shorthand equivalent to `[group("lint")]`.

When listing recipes, they will be sorted by group:

```
$ just --list
Available recipes:
(no group)
email-everyone # Not in a group
[lint]
cpp-lint
js-lint
rust-lint
[rust jobs]
rust-lint
```

The `--unsorted` flag prints recipes in their justfile order within each grouping:

```
$ just --list --unsorted
Available recipes:
(no group)
email-everyone # Not in a group
[lint]
js-lint
rust-lint
cpp-lint
[rust jobs]
rust-lint
```

Use `--groups` to see a list in alphabetical order of all the groups used in a justfile:

```
$ just --groups
lint
rust jobs
```

### Command Evaluation Using Backticks

Backticks can be used to store the result of commands:
Expand Down
55 changes: 38 additions & 17 deletions src/attribute.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ use super::*;
#[serde(rename_all = "kebab-case")]
pub(crate) enum Attribute<'src> {
Confirm(Option<StringLiteral<'src>>),
#[strum(disabled)]
Group {
name: StringLiteral<'src>,
},
Linux,
Macos,
NoCd,
Expand All @@ -16,30 +20,47 @@ pub(crate) enum Attribute<'src> {
}

impl<'src> Attribute<'src> {
pub(crate) fn from_name(name: Name) -> Option<Self> {
name.lexeme().parse().ok()
}
pub(crate) fn parse(
name: Name<'src>,
maybe_argument: Option<StringLiteral<'src>>,
) -> CompileResult<'src, Self> {
let name_str = name.lexeme();

pub(crate) fn name(&self) -> &'static str {
self.into()
Ok(match (name_str, maybe_argument) {
("group", Some(name)) => Self::Group { name },
("group", None) => {
return Err(name.error(CompileErrorKind::MissingAttributeArgument {
attribute_name: "group".into(),
}))
}
("confirm", argument) => Self::Confirm(argument),
(other_attribute, None) => other_attribute.parse().map_err(|_| {
name.error(CompileErrorKind::UnknownAttribute {
attribute: name_str,
})
})?,
(_other_attribute, Some(_)) => {
return Err(name.error(CompileErrorKind::UnexpectedAttributeArgument {
attribute: name_str,
}))
}
})
}

pub(crate) fn with_argument(
self,
name: Name<'src>,
argument: StringLiteral<'src>,
) -> CompileResult<'src, Self> {
match self {
Self::Confirm(_) => Ok(Self::Confirm(Some(argument))),
_ => Err(name.error(CompileErrorKind::UnexpectedAttributeArgument { attribute: self })),
pub(crate) fn name(&self) -> &'static str {
// Necessary because the Group variant is disabled for EnumString
if let Self::Group { .. } = self {
"group"
} else {
self.into()
}
}

fn argument(&self) -> Option<&StringLiteral> {
if let Self::Confirm(prompt) = self {
prompt.as_ref()
} else {
None
match self {
Self::Confirm(prompt) => prompt.as_ref(),
Self::Group { name } => Some(name),
_ => None,
}
}
}
Expand Down
6 changes: 4 additions & 2 deletions src/compile_error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,9 @@ impl Display for CompileError<'_> {
_ => character.escape_default().collect(),
}
),
MissingAttributeArgument { attribute_name } => {
write!(f, "Attribute `{attribute_name}` requires an argument")
}
MismatchedClosingDelimiter {
open,
open_line,
Expand Down Expand Up @@ -215,8 +218,7 @@ impl Display for CompileError<'_> {
UnexpectedAttributeArgument { attribute } => {
write!(
f,
"Attribute `{}` specified with argument but takes no arguments",
attribute.name(),
"Attribute `{attribute}` specified with argument but takes no arguments",
)
}
UnexpectedCharacter { expected } => write!(f, "Expected character `{expected}`"),
Expand Down
5 changes: 4 additions & 1 deletion src/compile_error_kind.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,9 @@ pub(crate) enum CompileErrorKind<'src> {
InvalidEscapeSequence {
character: char,
},
MissingAttributeArgument {
attribute_name: String,
},
MismatchedClosingDelimiter {
close: Delimiter,
open: Delimiter,
Expand All @@ -89,7 +92,7 @@ pub(crate) enum CompileErrorKind<'src> {
variable: &'src str,
},
UnexpectedAttributeArgument {
attribute: Attribute<'src>,
attribute: &'src str,
},
UnexpectedCharacter {
expected: char,
Expand Down
9 changes: 9 additions & 0 deletions src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ mod cmd {
pub(crate) const EDIT: &str = "EDIT";
pub(crate) const EVALUATE: &str = "EVALUATE";
pub(crate) const FORMAT: &str = "FORMAT";
pub(crate) const GROUPS: &str = "GROUPS";
pub(crate) const INIT: &str = "INIT";
pub(crate) const LIST: &str = "LIST";
pub(crate) const MAN: &str = "MAN";
Expand Down Expand Up @@ -417,6 +418,12 @@ impl Config {
.action(ArgAction::SetTrue)
.help("List available recipes and their arguments"),
)
.arg(
Arg::new(cmd::GROUPS)
.long("groups")
.action(ArgAction::SetTrue)
.help("List recipe groups")
)
.arg(
Arg::new(cmd::MAN)
.long("man")
Expand Down Expand Up @@ -649,6 +656,8 @@ impl Config {
Subcommand::Init
} else if matches.get_flag(cmd::LIST) {
Subcommand::List
} else if matches.get_flag(cmd::GROUPS) {
Subcommand::Groups
} else if matches.get_flag(cmd::MAN) {
Subcommand::Man
} else if let Some(name) = matches.get_one::<String>(cmd::SHOW).map(Into::into) {
Expand Down
12 changes: 11 additions & 1 deletion src/justfile.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use {super::*, serde::Serialize};
use {super::*, serde::Serialize, std::collections::HashSet};

#[derive(Debug)]
struct Invocation<'src: 'run, 'run> {
Expand Down Expand Up @@ -488,6 +488,16 @@ impl<'src> Justfile<'src> {

recipes
}

pub(crate) fn public_groups(&self) -> HashSet<String> {
self
.recipes
.values()
.map(AsRef::as_ref)
.filter(|recipe| recipe.is_public())
.flat_map(|recipe| recipe.groups())
.collect()
}
}

impl<'src> ColorDisplay for Justfile<'src> {
Expand Down
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,7 @@ mod keyword;
mod lexer;
mod line;
mod list;
mod list_recipes;
mod load_dotenv;
mod loader;
mod name;
Expand Down
Loading

0 comments on commit cd843c9

Please sign in to comment.