Allow private attribute on aliases (#1434)
This commit is contained in:
parent
c7acaa82cb
commit
fbe1c4c7a3
@ -2007,12 +2007,15 @@ $ just --summary
|
|||||||
test
|
test
|
||||||
```
|
```
|
||||||
|
|
||||||
The `[private]` attribute<sup>master</sup> may also be used to hide recipes without needing to change the name:
|
The `[private]` attribute<sup>master</sup> may also be used to hide recipes or aliases without needing to change the name:
|
||||||
|
|
||||||
```just
|
```just
|
||||||
[private]
|
[private]
|
||||||
foo:
|
foo:
|
||||||
|
|
||||||
|
[private]
|
||||||
|
alias b := bar
|
||||||
|
|
||||||
bar:
|
bar:
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -3,6 +3,7 @@ use super::*;
|
|||||||
/// An alias, e.g. `name := target`
|
/// An alias, e.g. `name := target`
|
||||||
#[derive(Debug, PartialEq, Clone, Serialize)]
|
#[derive(Debug, PartialEq, Clone, Serialize)]
|
||||||
pub(crate) struct Alias<'src, T = Rc<Recipe<'src>>> {
|
pub(crate) struct Alias<'src, T = Rc<Recipe<'src>>> {
|
||||||
|
pub(crate) attributes: BTreeSet<Attribute>,
|
||||||
pub(crate) name: Name<'src>,
|
pub(crate) name: Name<'src>,
|
||||||
#[serde(
|
#[serde(
|
||||||
bound(serialize = "T: Keyed<'src>"),
|
bound(serialize = "T: Keyed<'src>"),
|
||||||
@ -20,6 +21,7 @@ impl<'src> Alias<'src, Name<'src>> {
|
|||||||
assert_eq!(self.target.lexeme(), target.name.lexeme());
|
assert_eq!(self.target.lexeme(), target.name.lexeme());
|
||||||
|
|
||||||
Alias {
|
Alias {
|
||||||
|
attributes: self.attributes,
|
||||||
name: self.name,
|
name: self.name,
|
||||||
target,
|
target,
|
||||||
}
|
}
|
||||||
@ -28,7 +30,7 @@ impl<'src> Alias<'src, Name<'src>> {
|
|||||||
|
|
||||||
impl Alias<'_> {
|
impl Alias<'_> {
|
||||||
pub(crate) fn is_private(&self) -> bool {
|
pub(crate) fn is_private(&self) -> bool {
|
||||||
self.name.lexeme().starts_with('_')
|
self.name.lexeme().starts_with('_') || self.attributes.contains(&Attribute::Private)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,6 +2,8 @@ use super::*;
|
|||||||
|
|
||||||
use CompileErrorKind::*;
|
use CompileErrorKind::*;
|
||||||
|
|
||||||
|
const VALID_ALIAS_ATTRIBUTES: [Attribute; 1] = [Attribute::Private];
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub(crate) struct Analyzer<'src> {
|
pub(crate) struct Analyzer<'src> {
|
||||||
recipes: Table<'src, UnresolvedRecipe<'src>>,
|
recipes: Table<'src, UnresolvedRecipe<'src>>,
|
||||||
@ -195,6 +197,15 @@ impl<'src> Analyzer<'src> {
|
|||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for attr in &alias.attributes {
|
||||||
|
if !VALID_ALIAS_ATTRIBUTES.contains(attr) {
|
||||||
|
return Err(alias.name.token().error(AliasInvalidAttribute {
|
||||||
|
alias: name,
|
||||||
|
attr: *attr,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -24,6 +24,14 @@ impl Display for CompileError<'_> {
|
|||||||
use CompileErrorKind::*;
|
use CompileErrorKind::*;
|
||||||
|
|
||||||
match &*self.kind {
|
match &*self.kind {
|
||||||
|
AliasInvalidAttribute { alias, attr } => {
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
"Alias {} has an invalid attribute `{}`",
|
||||||
|
alias,
|
||||||
|
attr.to_str()
|
||||||
|
)?;
|
||||||
|
}
|
||||||
AliasShadowsRecipe { alias, recipe_line } => {
|
AliasShadowsRecipe { alias, recipe_line } => {
|
||||||
write!(
|
write!(
|
||||||
f,
|
f,
|
||||||
|
@ -2,6 +2,10 @@ use super::*;
|
|||||||
|
|
||||||
#[derive(Debug, PartialEq)]
|
#[derive(Debug, PartialEq)]
|
||||||
pub(crate) enum CompileErrorKind<'src> {
|
pub(crate) enum CompileErrorKind<'src> {
|
||||||
|
AliasInvalidAttribute {
|
||||||
|
alias: &'src str,
|
||||||
|
attr: Attribute,
|
||||||
|
},
|
||||||
AliasShadowsRecipe {
|
AliasShadowsRecipe {
|
||||||
alias: &'src str,
|
alias: &'src str,
|
||||||
recipe_line: usize,
|
recipe_line: usize,
|
||||||
|
@ -325,7 +325,7 @@ impl<'tokens, 'src> Parser<'tokens, 'src> {
|
|||||||
} else if self.next_is(Identifier) {
|
} else if self.next_is(Identifier) {
|
||||||
match Keyword::from_lexeme(next.lexeme()) {
|
match Keyword::from_lexeme(next.lexeme()) {
|
||||||
Some(Keyword::Alias) if self.next_are(&[Identifier, Identifier, ColonEquals]) => {
|
Some(Keyword::Alias) if self.next_are(&[Identifier, Identifier, ColonEquals]) => {
|
||||||
items.push(Item::Alias(self.parse_alias()?));
|
items.push(Item::Alias(self.parse_alias(BTreeSet::new())?));
|
||||||
}
|
}
|
||||||
Some(Keyword::Export) if self.next_are(&[Identifier, Identifier, ColonEquals]) => {
|
Some(Keyword::Export) if self.next_are(&[Identifier, Identifier, ColonEquals]) => {
|
||||||
self.presume_keyword(Keyword::Export)?;
|
self.presume_keyword(Keyword::Export)?;
|
||||||
@ -361,9 +361,17 @@ impl<'tokens, 'src> Parser<'tokens, 'src> {
|
|||||||
BTreeSet::new(),
|
BTreeSet::new(),
|
||||||
)?));
|
)?));
|
||||||
} else if let Some(attributes) = self.parse_attributes()? {
|
} else if let Some(attributes) = self.parse_attributes()? {
|
||||||
let quiet = self.accepted(At)?;
|
let next_keyword = Keyword::from_lexeme(self.next()?.lexeme());
|
||||||
let doc = pop_doc_comment(&mut items, eol_since_last_comment);
|
match next_keyword {
|
||||||
items.push(Item::Recipe(self.parse_recipe(doc, quiet, attributes)?));
|
Some(Keyword::Alias) if self.next_are(&[Identifier, Identifier, ColonEquals]) => {
|
||||||
|
items.push(Item::Alias(self.parse_alias(attributes)?));
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
let quiet = self.accepted(At)?;
|
||||||
|
let doc = pop_doc_comment(&mut items, eol_since_last_comment);
|
||||||
|
items.push(Item::Recipe(self.parse_recipe(doc, quiet, attributes)?));
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
return Err(self.unexpected_token()?);
|
return Err(self.unexpected_token()?);
|
||||||
}
|
}
|
||||||
@ -383,13 +391,20 @@ impl<'tokens, 'src> Parser<'tokens, 'src> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Parse an alias, e.g `alias name := target`
|
/// Parse an alias, e.g `alias name := target`
|
||||||
fn parse_alias(&mut self) -> CompileResult<'src, Alias<'src, Name<'src>>> {
|
fn parse_alias(
|
||||||
|
&mut self,
|
||||||
|
attributes: BTreeSet<Attribute>,
|
||||||
|
) -> CompileResult<'src, Alias<'src, Name<'src>>> {
|
||||||
self.presume_keyword(Keyword::Alias)?;
|
self.presume_keyword(Keyword::Alias)?;
|
||||||
let name = self.parse_name()?;
|
let name = self.parse_name()?;
|
||||||
self.presume_any(&[Equals, ColonEquals])?;
|
self.presume_any(&[Equals, ColonEquals])?;
|
||||||
let target = self.parse_name()?;
|
let target = self.parse_name()?;
|
||||||
self.expect_eol()?;
|
self.expect_eol()?;
|
||||||
Ok(Alias { name, target })
|
Ok(Alias {
|
||||||
|
attributes,
|
||||||
|
name,
|
||||||
|
target,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse an assignment, e.g. `foo := bar`
|
/// Parse an assignment, e.g. `foo := bar`
|
||||||
@ -978,6 +993,12 @@ mod tests {
|
|||||||
tree: (justfile (alias t test)),
|
tree: (justfile (alias t test)),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
test! {
|
||||||
|
name: alias_with_attributee,
|
||||||
|
text: "[private]\nalias t := test",
|
||||||
|
tree: (justfile (alias t test)),
|
||||||
|
}
|
||||||
|
|
||||||
test! {
|
test! {
|
||||||
name: aliases_multiple,
|
name: aliases_multiple,
|
||||||
text: "alias t := test\nalias b := build",
|
text: "alias t := test\nalias b := build",
|
||||||
@ -996,6 +1017,18 @@ mod tests {
|
|||||||
),
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
test! {
|
||||||
|
name: recipe_named_alias,
|
||||||
|
text: r#"
|
||||||
|
[private]
|
||||||
|
alias:
|
||||||
|
echo 'echoing alias'
|
||||||
|
"#,
|
||||||
|
tree: (justfile
|
||||||
|
(recipe alias (body ("echo 'echoing alias'")))
|
||||||
|
),
|
||||||
|
}
|
||||||
|
|
||||||
test! {
|
test! {
|
||||||
name: export,
|
name: export,
|
||||||
text: r#"export x := "hello""#,
|
text: r#"export x := "hello""#,
|
||||||
|
@ -1,5 +1,17 @@
|
|||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
|
test! {
|
||||||
|
name: invalid_alias_attribute,
|
||||||
|
justfile: "[private]\n[linux]\nalias t := test\n\ntest:\n",
|
||||||
|
stderr: "
|
||||||
|
error: Alias t has an invalid attribute `linux`
|
||||||
|
|
|
||||||
|
3 | alias t := test
|
||||||
|
| ^
|
||||||
|
",
|
||||||
|
status: EXIT_FAILURE,
|
||||||
|
}
|
||||||
|
|
||||||
test! {
|
test! {
|
||||||
name: expected_keyword,
|
name: expected_keyword,
|
||||||
justfile: "foo := if '' == '' { '' } arlo { '' }",
|
justfile: "foo := if '' == '' { '' } arlo { '' }",
|
||||||
|
@ -22,6 +22,7 @@ fn alias() {
|
|||||||
"f": {
|
"f": {
|
||||||
"name": "f",
|
"name": "f",
|
||||||
"target": "foo",
|
"target": "foo",
|
||||||
|
"attributes": [],
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"assignments": {},
|
"assignments": {},
|
||||||
@ -299,6 +300,7 @@ fn duplicate_recipes() {
|
|||||||
"first": "foo",
|
"first": "foo",
|
||||||
"aliases": {
|
"aliases": {
|
||||||
"f": {
|
"f": {
|
||||||
|
"attributes": [],
|
||||||
"name": "f",
|
"name": "f",
|
||||||
"target": "foo",
|
"target": "foo",
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn attribute() {
|
fn private_attribute_for_recipe() {
|
||||||
Test::new()
|
Test::new()
|
||||||
.justfile(
|
.justfile(
|
||||||
"
|
"
|
||||||
@ -17,3 +17,24 @@ fn attribute() {
|
|||||||
)
|
)
|
||||||
.run();
|
.run();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn private_attribute_for_alias() {
|
||||||
|
Test::new()
|
||||||
|
.justfile(
|
||||||
|
"
|
||||||
|
[private]
|
||||||
|
alias f := foo
|
||||||
|
|
||||||
|
foo:
|
||||||
|
",
|
||||||
|
)
|
||||||
|
.args(&["--list"])
|
||||||
|
.stdout(
|
||||||
|
"
|
||||||
|
Available recipes:
|
||||||
|
foo
|
||||||
|
",
|
||||||
|
)
|
||||||
|
.run();
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user