Skip to content

Commit

Permalink
refactor: rename exclusive groups to restricted groups.
Browse files Browse the repository at this point in the history
fix: restricted groups incorrectly throwing errors for usage violations that do not occur.
  • Loading branch information
darvil82 committed Dec 26, 2023
1 parent 2247c6b commit 3e6d8f0
Show file tree
Hide file tree
Showing 9 changed files with 58 additions and 58 deletions.
2 changes: 1 addition & 1 deletion build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ plugins {
}

group = "com.darvil"
version = "0.2.0"
version = "0.2.1"
description = "Command line argument parser"

dependencies {
Expand Down
22 changes: 11 additions & 11 deletions src/main/java/lanat/Argument.java
Original file line number Diff line number Diff line change
Expand Up @@ -406,11 +406,12 @@ public void setRepresentationColor(@NotNull Color color) {

/* no, | is not a typo. We don't want the OR operator to short-circuit, we want all of them to be evaluated
* because the methods have side effects (they add errors to the parser) */
TInner returnValue = (finalValue == null | !this.finishParsing$checkExclusivity() | !this.finishParsing$checkUsageCount())
TInner returnValue = (finalValue == null | !this.finishParsing$checkGroupRestrictions() | !this.finishParsing$checkUsageCount())
? defaultValue
: finalValue;

if (this.parentGroup != null) this.parentGroup.setArgUsed();
if (this.parentGroup != null && this.getUsageCount() >= 1)
this.parentGroup.setArgUsed();

// if the argument type has a value defined (even if it wasn't used), use that. Otherwise, use the default value
return returnValue;
Expand Down Expand Up @@ -442,20 +443,19 @@ public void setRepresentationColor(@NotNull Color color) {
}

/**
* Checks if the argument is part of an exclusive group, and if so, checks if there is any violation of exclusivity
* Checks if the argument is part of a restricted group, and if so, checks if there is any violation of restrictions
* in the group hierarchy.
*
* @return {@code true} if there is no violation of exclusivity in the group hierarchy.
* @return {@code true} if there is no violation of restrictions in the group hierarchy.
*/
private boolean finishParsing$checkExclusivity() {
// check if the parent group of this argument is exclusive, and if so, check if any other argument in it has been used
private boolean finishParsing$checkGroupRestrictions() {
// check if the parent group of this argument is restricted, and if so, check if any other argument in it has been used
if (this.parentGroup == null || this.getUsageCount() == 0) return true;

ArgumentGroup exclusivityResult = this.parentGroup.checkExclusivity(null);
if (exclusivityResult == null) return true;
ArgumentGroup restrictionViolator = this.parentGroup.getRestrictionViolator(null);
if (restrictionViolator == null) return true;

this.parentCommand.getParser().addError(new ParseErrors.MultipleArgsInExclusiveGroupUsedError(
this.argType.getLastTokensIndicesPair(), exclusivityResult
this.parentCommand.getParser().addError(new ParseErrors.MultipleArgsInRestrictedGroupUsedError(
this.argType.getLastTokensIndicesPair(), restrictionViolator
));
return false;
}
Expand Down
50 changes: 25 additions & 25 deletions src/main/java/lanat/ArgumentGroup.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,25 +13,25 @@
/**
* <h2>Argument Group</h2>
* <p>
* Represents a group of arguments. This is used to group arguments together, and to set exclusivity between them.
* When a group is exclusive, it means that only one argument in it can be used at a time.
* Represents a group of arguments. This is used to group arguments together, and to set a restriction between them.
* When a group is restricted, it means that only one argument in it can be used at a time.
* <p>
* Groups can also be used to simply indicate arguments that are related to each other, and to set a description
* to this relation. This is useful for the help message representation.
* <p>
* Groups can be nested, meaning that a group can contain other groups. This is useful for setting exclusivity between
* Groups can be nested, meaning that a group can contain other groups. This is useful for setting restrictions between
* arguments that are in different groups. For example, given the following group tree:
* <pre>
* +-----------------------+
* | Group 1 (exclusive) |
* | Group 1 (restricted) |
* |-----------------------|
* | Argument 1 |
* +-----------------------+
* |
* +---------------------------+
* | |
* +---------------+ +-------------------------+
* | Group 2 | | Group 3 (exclusive) |
* | Group 2 | | Group 3 (restricted) |
* |---------------| |-------------------------|
* | Argument 2.1 | | Argument 3.1 |
* | Argument 2.2 | | Argument 3.2 |
Expand All @@ -40,14 +40,14 @@
* <ul>
* <li>
* If {@code Argument 1} is used, then none of the arguments in the child groups can be used, because {@code Group 1}
* is exclusive.
* is restricted.
* </li>
* <li>
* If {@code Argument 3.1} is used, then none of the arguments in the rest of the tree can be used, because
* both {@code Group 3} and its parent {@code Group 1} are exclusive.
* both {@code Group 3} and its parent {@code Group 1} are restricted.
* </li>
* <li>
* If {@code Argument 2.1} is used, {@code Argument 2.2} can still be used, because {@code Group 2} is not exclusive.
* If {@code Argument 2.1} is used, {@code Argument 2.2} can still be used, because {@code Group 2} is not restricted.
* No other arguments in the tree can be used though.
* </li>
* </ul>
Expand Down Expand Up @@ -85,11 +85,11 @@ public class ArgumentGroup
* them.
*/
private final @NotNull List<@NotNull ArgumentGroup> subGroups = new ArrayList<>();
private boolean isExclusive = false;
private boolean isRestricted = false;

/**
* When set to {@code true}, indicates that one argument in this group has been used. This is used when later
* checking for exclusivity in the groups tree at {@link ArgumentGroup#checkExclusivity(ArgumentGroup)}
* checking for restrictions in the groups tree at {@link ArgumentGroup#getRestrictionViolator(ArgumentGroup)}
*/
private boolean argumentUsed = false;

Expand Down Expand Up @@ -184,42 +184,42 @@ public Command getParentCommand() {
}

/**
* Sets this group to be exclusive, meaning that only one argument in it can be used at a time.
* @see ArgumentGroup#isExclusive()
* Sets this group to be restricted, meaning that only one argument in it can be used at a time.
* @see ArgumentGroup#isRestricted()
*/
public void setExclusive(boolean isExclusive) {
this.isExclusive = isExclusive;
public void setRestricted(boolean isRestricted) {
this.isRestricted = isRestricted;
}

/**
* Returns {@code true} if this group is exclusive.
* @return {@code true} if this group is exclusive.
* @see ArgumentGroup#setExclusive(boolean)
* Returns {@code true} if this group is restricted.
* @return {@code true} if this group is restricted.
* @see ArgumentGroup#setRestricted(boolean)
*/
public boolean isExclusive() {
return this.isExclusive;
public boolean isRestricted() {
return this.isRestricted;
}

/**
* Checks if there is any violation of exclusivity in this group's tree, from this group to the root. This is done
* Checks if there is any violation of restrictions in this group's tree, from this group to the root. This is done
* by checking if this or any of the group's siblings have been used (except for the childCallee, which is the group
* that called this method). If none of them have been used, the parent group is checked, and so on.
*
* @param childCallee The group that called this method. This is used to avoid checking the group that called this
* method, because it is the one that is being checked for exclusivity. This can be {@code null} if this is
* method, because it is the one that is being checked for restriction. This can be {@code null} if this is
* the first call to this method.
* @return The group that caused the violation, or {@code null} if there is no violation.
*/
@Nullable ArgumentGroup checkExclusivity(@Nullable ArgumentGroup childCallee) {
@Nullable ArgumentGroup getRestrictionViolator(@Nullable ArgumentGroup childCallee) {
if (
this.isExclusive && (
this.isRestricted && (
this.argumentUsed || this.subGroups.stream().filter(g -> g != childCallee).anyMatch(g -> g.argumentUsed)
)
)
return this;

if (this.parentGroup != null)
return this.parentGroup.checkExclusivity(this);
return this.parentGroup.getRestrictionViolator(this);

return null;
}
Expand All @@ -234,7 +234,7 @@ public boolean isEmpty() {


/**
* Marks that an argument in this group has been used. This is used to later check for exclusivity.
* Marks that an argument in this group has been used. This is used to later check for restrictions.
* This also marks the parent group as used, and so on until reaching the root of the groups tree, thus marking the
* path of the used argument.
*/
Expand Down
16 changes: 8 additions & 8 deletions src/main/java/lanat/helpRepresentation/ArgumentGroupRepr.java
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ private ArgumentGroupRepr() {}
return null;

final var name = new TextFormatter(group.getName() + ':').addFormat(FormatOption.BOLD);
if (group.isExclusive())
if (group.isRestricted())
name.addFormat(FormatOption.UNDERLINE);

return '\n' + name.toString() + '\n' + HelpFormatter.indent(description, group);
Expand Down Expand Up @@ -62,7 +62,7 @@ private ArgumentGroupRepr() {}
if (description == null && argumentDescriptions.isEmpty())
return "";

if (group.isExclusive())
if (group.isRestricted())
name.addFormat(FormatOption.UNDERLINE);

if (description != null)
Expand Down Expand Up @@ -92,8 +92,8 @@ public static String getRepresentation(@NotNull ArgumentGroup group) {
// its empty, nothing to append
if (group.isEmpty()) return "";

// if this group isn't exclusive, we just want to append the arguments, basically
if (group.isExclusive())
// if this group isn't restricted, we just want to append the arguments, basically
if (group.isRestricted())
buff.append('(');

final var arguments = Argument.sortByPriority(group.getArguments());
Expand All @@ -103,7 +103,7 @@ public static String getRepresentation(@NotNull ArgumentGroup group) {
buff.append(ArgumentRepr.getRepresentation(arg));
if (i < arguments.size() - 1) {
buff.append(' ');
if (group.isExclusive())
if (group.isRestricted())
buff.append('|').append(' ');
}
}
Expand All @@ -112,7 +112,7 @@ public static String getRepresentation(@NotNull ArgumentGroup group) {

if (!arguments.isEmpty() && !groups.isEmpty()) {
buff.append(' ');
if (group.isExclusive())
if (group.isRestricted())
buff.append("| ");
}

Expand All @@ -121,12 +121,12 @@ public static String getRepresentation(@NotNull ArgumentGroup group) {
buff.append(ArgumentGroupRepr.getRepresentation(grp)); // append the group's representation recursively
if (i < groups.size() - 1) {
buff.append(' ');
if (grp.isExclusive())
if (grp.isRestricted())
buff.append('|').append(' ');
}
}

if (group.isExclusive())
if (group.isRestricted())
buff.append(')');

return buff.toString();
Expand Down
8 changes: 4 additions & 4 deletions src/main/java/lanat/parsing/errors/ParseErrors.java
Original file line number Diff line number Diff line change
Expand Up @@ -152,19 +152,19 @@ public void handle(@NotNull ErrorFormattingContext fmt, @NotNull ParseErrorConte
}

/**
* Error that occurs when multiple arguments in an exclusive group are used.
* Error that occurs when multiple arguments in a restricted group are used.
* @param indicesPair The indices of the tokens that caused the error. (start, end)
* @param group The exclusive group that contains the arguments.
* @param group The restricted group that contains the arguments.
*/
public record MultipleArgsInExclusiveGroupUsedError(
public record MultipleArgsInRestrictedGroupUsedError(
@NotNull Pair<Integer, Integer> indicesPair,
@NotNull ArgumentGroup group
) implements Error.ParseError
{
@Override
public void handle(@NotNull ErrorFormattingContext fmt, @NotNull ParseErrorContext ctx) {
fmt
.withContent("Multiple arguments in exclusive group '" + this.group.getName() + "' used.")
.withContent("Multiple arguments in restricted group '" + this.group.getName() + "' used.")
.highlight(this.indicesPair.first(), this.indicesPair.second(), false);
}
}
Expand Down
4 changes: 2 additions & 2 deletions src/test/java/lanat/test/UnitTests.java
Original file line number Diff line number Diff line change
Expand Up @@ -82,8 +82,8 @@ protected TestingParser setParser() {
this.addCommand(new Command("subCommand2") {{
this.setErrorCode(0b1000);

this.addGroup(new ArgumentGroup("exclusive-group") {{
this.setExclusive(true);
this.addGroup(new ArgumentGroup("restricted-group") {{
this.setRestricted(true);
this.addArgument(Argument.createOfBoolType("extra"));
this.addArgument(Argument.create(new IntegerArgumentType(), 'c').positional());
}});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ protected boolean checkFile(@NotNull File file) {
@InitDef
public static void afterInit(@NotNull Command cmd) {
cmd.addGroup(new ArgumentGroup("test-group") {{
this.setExclusive(true);
this.setRestricted(true);
this.addArgument(cmd.getArgument("string"));
this.addArgument(cmd.getArgument("number"));
}});
Expand Down
6 changes: 3 additions & 3 deletions src/test/java/lanat/test/units/TestArgumentGroups.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ protected TestingParser setParser() {
final var parser = super.setParser();

parser.addGroup(new ArgumentGroup("group") {{
this.setExclusive(true);
this.setRestricted(true);
this.addArgument(Argument.createOfBoolType("group-arg"));
this.addArgument(Argument.createOfBoolType("group-arg2"));
}});
Expand All @@ -24,8 +24,8 @@ protected TestingParser setParser() {
}

@Test
@DisplayName("Test exclusive group")
public void testExclusiveGroup() {
@DisplayName("Test restricted group")
public void testRestrictedGroup() {
var parsedArgs = this.parser.parseGetValues("--group-arg --group-arg2");
assertEquals(Boolean.TRUE, parsedArgs.<Boolean>get("group-arg").orElse(null));
assertEquals(Boolean.FALSE, parsedArgs.<Boolean>get("group-arg2").orElse(null)); // group-arg2 should not be present
Expand Down
6 changes: 3 additions & 3 deletions src/test/java/lanat/test/units/TestTerminalOutput.java
Original file line number Diff line number Diff line change
Expand Up @@ -119,12 +119,12 @@ public void testIncorrectUsageCount() {
}

@Test
@DisplayName("Test group exclusivity error")
public void testGroupExclusivityError() {
@DisplayName("Test group restriction error")
public void testGroupRestrictionError() {
this.assertErrorOutput("foo subCommand2 --extra --c 5", """
ERROR
Testing foo subCommand2 --extra -> --c 5 <-
Multiple arguments in exclusive group 'exclusive-group' used.""");
Multiple arguments in restricted group 'restricted-group' used.""");
}

@Test
Expand Down

0 comments on commit 3e6d8f0

Please sign in to comment.