Skip to content

Commit

Permalink
not() attribute
Browse files Browse the repository at this point in the history
  • Loading branch information
neunenak committed Dec 17, 2024
1 parent 20192d4 commit 44ea002
Show file tree
Hide file tree
Showing 7 changed files with 198 additions and 46 deletions.
78 changes: 47 additions & 31 deletions src/attribute.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,17 +13,17 @@ pub(crate) enum Attribute<'src> {
Doc(Option<StringLiteral<'src>>),
Extension(StringLiteral<'src>),
Group(StringLiteral<'src>),
Linux,
Macos,
Linux { inverted: bool },
Macos { inverted: bool },
NoCd,
NoExitMessage,
NoQuiet,
Openbsd,
Openbsd { inverted: bool },
PositionalArguments,
Private,
Script(Option<Interpreter<'src>>),
Unix,
Windows,
Unix { inverted: bool },
Windows { inverted: bool },
WorkingDirectory(StringLiteral<'src>),
}

Expand Down Expand Up @@ -51,6 +51,7 @@ impl<'src> Attribute<'src> {
pub(crate) fn new(
name: Name<'src>,
arguments: Vec<StringLiteral<'src>>,
inverted: bool,
) -> CompileResult<'src, Self> {
let discriminant = name
.lexeme()
Expand All @@ -75,29 +76,38 @@ impl<'src> Attribute<'src> {
);
}

Ok(match discriminant {
AttributeDiscriminant::Confirm => Self::Confirm(arguments.into_iter().next()),
AttributeDiscriminant::Doc => Self::Doc(arguments.into_iter().next()),
AttributeDiscriminant::Extension => Self::Extension(arguments.into_iter().next().unwrap()),
AttributeDiscriminant::Group => Self::Group(arguments.into_iter().next().unwrap()),
AttributeDiscriminant::Linux => Self::Linux,
AttributeDiscriminant::Macos => Self::Macos,
AttributeDiscriminant::NoCd => Self::NoCd,
AttributeDiscriminant::NoExitMessage => Self::NoExitMessage,
AttributeDiscriminant::NoQuiet => Self::NoQuiet,
AttributeDiscriminant::Openbsd => Self::Openbsd,
AttributeDiscriminant::PositionalArguments => Self::PositionalArguments,
AttributeDiscriminant::Private => Self::Private,
AttributeDiscriminant::Script => Self::Script({
Ok(match (inverted, discriminant) {
(inverted, AttributeDiscriminant::Linux) => Self::Linux { inverted },
(inverted, AttributeDiscriminant::Macos) => Self::Macos { inverted },
(inverted, AttributeDiscriminant::Unix) => Self::Unix { inverted },
(inverted, AttributeDiscriminant::Windows) => Self::Windows { inverted },
(inverted, AttributeDiscriminant::Openbsd) => Self::Openbsd { inverted },

(true, _attr) => {
return Err(name.error(CompileErrorKind::InvalidInvertedAttribute {
attr_name: name.lexeme(),
}))
}

(false, AttributeDiscriminant::Confirm) => Self::Confirm(arguments.into_iter().next()),
(false, AttributeDiscriminant::Doc) => Self::Doc(arguments.into_iter().next()),
(false, AttributeDiscriminant::Extension) => {
Self::Extension(arguments.into_iter().next().unwrap())
}
(false, AttributeDiscriminant::Group) => Self::Group(arguments.into_iter().next().unwrap()),
(false, AttributeDiscriminant::NoCd) => Self::NoCd,
(false, AttributeDiscriminant::NoExitMessage) => Self::NoExitMessage,
(false, AttributeDiscriminant::NoQuiet) => Self::NoQuiet,
(false, AttributeDiscriminant::PositionalArguments) => Self::PositionalArguments,
(false, AttributeDiscriminant::Private) => Self::Private,
(false, AttributeDiscriminant::Script) => Self::Script({
let mut arguments = arguments.into_iter();
arguments.next().map(|command| Interpreter {
command,
arguments: arguments.collect(),
})
}),
AttributeDiscriminant::Unix => Self::Unix,
AttributeDiscriminant::Windows => Self::Windows,
AttributeDiscriminant::WorkingDirectory => {
(false, AttributeDiscriminant::WorkingDirectory) => {
Self::WorkingDirectory(arguments.into_iter().next().unwrap())
}
})
Expand All @@ -118,28 +128,34 @@ impl<'src> Attribute<'src> {

impl Display for Attribute<'_> {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "{}", self.name())?;
let name = 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::WorkingDirectory(argument) => write!(f, "{name}({argument})")?,
Self::Script(Some(shell)) => write!(f, "{name}({shell})")?,
Self::Linux { inverted }
| Self::Macos { inverted }
| Self::Unix { inverted }
| Self::Openbsd { inverted }
| Self::Windows { inverted } => {
if *inverted {
write!(f, "not({name})")?;
} else {
write!(f, "{name}")?;
}
}
Self::Confirm(None)
| Self::Doc(None)
| Self::Linux
| Self::Macos
| Self::NoCd
| Self::NoExitMessage
| Self::NoQuiet
| Self::Openbsd
| Self::PositionalArguments
| Self::Private
| Self::Script(None)
| Self::Unix
| Self::Windows => {}
| Self::Script(None) => write!(f, "{name}")?,
}

Ok(())
Expand Down
28 changes: 28 additions & 0 deletions src/attribute_set.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
use {super::*, std::collections};

#[derive(Debug, Copy, Clone)]
pub(crate) enum InvertedStatus {
Normal,
Inverted,
}

#[derive(Default, Debug, Clone, PartialEq, Serialize)]
pub(crate) struct AttributeSet<'src>(BTreeSet<Attribute<'src>>);

Expand All @@ -12,6 +18,28 @@ impl<'src> AttributeSet<'src> {
self.0.iter().any(|attr| attr.discriminant() == target)
}

pub(crate) fn contains_invertible(
&self,
target: AttributeDiscriminant,
) -> Option<InvertedStatus> {
self.get(target).and_then(|attr| {
Some(match attr {
Attribute::Linux { inverted }
| Attribute::Macos { inverted }
| Attribute::Openbsd { inverted }
| Attribute::Unix { inverted }
| Attribute::Windows { inverted } => {
if *inverted {
InvertedStatus::Inverted
} else {
InvertedStatus::Normal
}
}
_ => return None,
})
})
}

pub(crate) fn get(&self, discriminant: AttributeDiscriminant) -> Option<&Attribute<'src>> {
self
.0
Expand Down
3 changes: 3 additions & 0 deletions src/compile_error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,9 @@ impl Display for CompileError<'_> {
_ => character.escape_default().collect(),
}
),
InvalidInvertedAttribute { attr_name } => {
write!(f, "{attr_name} cannot be inverted with `not()`")
}
MismatchedClosingDelimiter {
open,
open_line,
Expand Down
3 changes: 3 additions & 0 deletions src/compile_error_kind.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,9 @@ pub(crate) enum CompileErrorKind<'src> {
InvalidEscapeSequence {
character: char,
},
InvalidInvertedAttribute {
attr_name: &'src str,
},
MismatchedClosingDelimiter {
close: Delimiter,
open: Delimiter,
Expand Down
26 changes: 24 additions & 2 deletions src/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1135,7 +1135,18 @@ impl<'run, 'src> Parser<'run, 'src> {
token.get_or_insert(bracket);

loop {
let name = self.parse_name()?;
let (name, inverted) = {
let mut i = false;
let mut n = self.parse_name()?;
if n.lexeme() == "not" {
i = true;
self.expect(ParenL)?;
n = self.parse_name()?;
self.expect(ParenR)?;
}

(n, i)
};

let mut arguments = Vec::new();

Expand All @@ -1152,7 +1163,7 @@ impl<'run, 'src> Parser<'run, 'src> {
self.expect(ParenR)?;
}

let attribute = Attribute::new(name, arguments)?;
let attribute = Attribute::new(name, arguments, inverted)?;

let first = attributes.get(&attribute).or_else(|| {
if attribute.repeatable() {
Expand Down Expand Up @@ -2668,6 +2679,17 @@ mod tests {
kind: UnknownAttribute { attribute: "unknown" },
}

error! {
name: invalid_invertable_attribute,
input: "[not(private)]\nsome_recipe:\n @exit 3",
offset: 5,
line: 0,
column: 5,
width: 7,
kind: InvalidInvertedAttribute { attr_name: "private" },

}

error! {
name: set_unknown,
input: "set shall := []",
Expand Down
88 changes: 75 additions & 13 deletions src/recipe.rs
Original file line number Diff line number Diff line change
Expand Up @@ -116,19 +116,81 @@ impl<'src, D> Recipe<'src, D> {
}

pub(crate) fn enabled(&self) -> bool {
let linux = self.attributes.contains(AttributeDiscriminant::Linux);
let macos = self.attributes.contains(AttributeDiscriminant::Macos);
let openbsd = self.attributes.contains(AttributeDiscriminant::Openbsd);
let unix = self.attributes.contains(AttributeDiscriminant::Unix);
let windows = self.attributes.contains(AttributeDiscriminant::Windows);

(!windows && !linux && !macos && !openbsd && !unix)
|| (cfg!(target_os = "linux") && (linux || unix))
|| (cfg!(target_os = "macos") && (macos || unix))
|| (cfg!(target_os = "openbsd") && (openbsd || unix))
|| (cfg!(target_os = "windows") && windows)
|| (cfg!(unix) && unix)
|| (cfg!(windows) && windows)
use attribute_set::InvertedStatus;

struct Systems {
linux: bool,
macos: bool,
openbsd: bool,
unix: bool,
windows: bool,
}

let linux = self
.attributes
.contains_invertible(AttributeDiscriminant::Linux);
let macos = self
.attributes
.contains_invertible(AttributeDiscriminant::Macos);
let openbsd = self
.attributes
.contains_invertible(AttributeDiscriminant::Openbsd);
let unix = self
.attributes
.contains_invertible(AttributeDiscriminant::Unix);
let windows = self
.attributes
.contains_invertible(AttributeDiscriminant::Windows);

if [linux, macos, openbsd, unix, windows]
.into_iter()
.all(|x| x.is_none())
{
return true;
}

let systems = Systems {
linux: matches!(linux, Some(InvertedStatus::Normal)),
macos: matches!(macos, Some(InvertedStatus::Normal)),
openbsd: matches!(openbsd, Some(InvertedStatus::Normal)),
unix: matches!(unix, Some(InvertedStatus::Normal)),
windows: matches!(windows, Some(InvertedStatus::Normal)),
};

let disabled = Systems {
linux: matches!(linux, Some(InvertedStatus::Inverted)),
macos: matches!(macos, Some(InvertedStatus::Inverted)),
openbsd: matches!(openbsd, Some(InvertedStatus::Inverted)),
unix: matches!(unix, Some(InvertedStatus::Inverted)),
windows: matches!(windows, Some(InvertedStatus::Inverted)),
};

if cfg!(target_os = "linux") {
return !(disabled.linux || disabled.unix)
&& ((systems.linux || systems.unix)
|| (!systems.openbsd && !systems.windows && !systems.macos));
}

if cfg!(target_os = "openbsd") {
return !disabled.openbsd
&& (systems.openbsd || (!systems.windows && !systems.macos && !systems.linux));
}

if cfg!(target_os = "windows") || cfg!(windows) {
return !disabled.windows
&& (systems.windows
|| (!systems.openbsd && !systems.unix && !systems.macos && !systems.linux));
}

if cfg!(target_os = "macos") {
return !disabled.macos
&& (systems.macos || (!systems.openbsd && !systems.windows && !systems.linux));
}

if cfg!(unix) {
return !(disabled.unix) && (systems.unix || !systems.windows);
}
false
}

fn print_exit_message(&self) -> bool {
Expand Down
18 changes: 18 additions & 0 deletions tests/attributes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -254,3 +254,21 @@ fn duplicate_non_repeatable_attributes_are_forbidden() {
.status(EXIT_FAILURE)
.run();
}

#[test]
fn invertible_attributes() {
Test::new()
.justfile(
"
[not(windows)]
non-windows-recipe:
exit 0
[windows]
windows-recipe:
exit 0
",
)
.stderr("exit 0\n")
.run();
}

0 comments on commit 44ea002

Please sign in to comment.