Skip to content

Commit

Permalink
Add positional argument support to built-in restrictions (#91)
Browse files Browse the repository at this point in the history
All built-in restrictions should now support positional arguments
  • Loading branch information
rvesse committed Apr 29, 2019
1 parent bb680c1 commit bfe802e
Show file tree
Hide file tree
Showing 6 changed files with 75 additions and 5 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import java.util.TreeSet;

import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.IterableUtils;

import com.github.rvesse.airline.help.sections.HelpFormat;
import com.github.rvesse.airline.help.sections.HelpHint;
Expand All @@ -43,13 +44,15 @@ public class PartialRestriction extends AbstractCommonRestriction implements Hel
private PartialRestriction(OptionRestriction optionRestriction) {
this.optionRestriction = optionRestriction;
this.argumentsRestriction = optionRestriction instanceof ArgumentsRestriction
? (ArgumentsRestriction) optionRestriction : null;
? (ArgumentsRestriction) optionRestriction
: null;
this.hint = optionRestriction instanceof HelpHint ? (HelpHint) optionRestriction : null;
}

private PartialRestriction(ArgumentsRestriction argumentsRestriction) {
this.optionRestriction = argumentsRestriction instanceof OptionRestriction
? (OptionRestriction) argumentsRestriction : null;
? (OptionRestriction) argumentsRestriction
: null;
this.argumentsRestriction = argumentsRestriction;
this.hint = argumentsRestriction instanceof HelpHint ? (HelpHint) argumentsRestriction : null;
}
Expand Down Expand Up @@ -79,8 +82,8 @@ public PartialRestriction(Collection<Integer> indices, ArgumentsRestriction argu
}

private <T> boolean isApplicableToOption(ParseState<T> state, OptionMetadata option) {
int index = CollectionUtils.countMatches(state.getParsedOptions(), new ParsedOptionFinder(option))
% option.getArity();
int index = (int) (IterableUtils.countMatches(state.getParsedOptions(), new ParsedOptionFinder(option))
% option.getArity());
return indices.contains(index);
}

Expand All @@ -104,6 +107,9 @@ public <T> void postValidate(ParseState<T> state, OptionMetadata option, Object
this.optionRestriction.postValidate(state, option, value);
}

// NB - Partial restriction make no sense for positional arguments which
// only ever take a single value and thus are not applied

private <T> boolean isApplicableToArgument(ParseState<T> state) {
int index = state.getParsedArguments().size();
return indices.contains(index);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import com.github.rvesse.airline.help.sections.HelpHint;
import com.github.rvesse.airline.model.ArgumentsMetadata;
import com.github.rvesse.airline.model.OptionMetadata;
import com.github.rvesse.airline.model.PositionalArgumentMetadata;
import com.github.rvesse.airline.parser.ParseState;
import com.github.rvesse.airline.parser.errors.ParseRestrictionViolatedException;
import com.github.rvesse.airline.restrictions.AbstractCommonRestriction;
Expand Down Expand Up @@ -169,6 +170,12 @@ public <T> void preValidate(ParseState<T> state, ArgumentsMetadata arguments, St
this.validate(String.format("Argument '%s'", AbstractCommonRestriction.getArgumentTitle(state, arguments)),
value);
}

@Override
public <T> void preValidate(ParseState<T> state, PositionalArgumentMetadata arguments, String value) {
this.validate(String.format("Positional Argument %d ('%s')", arguments.getZeroBasedPosition(), arguments.getTitle()),
value);
}

@Override
public String getPreamble() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import com.github.rvesse.airline.help.sections.HelpHint;
import com.github.rvesse.airline.model.ArgumentsMetadata;
import com.github.rvesse.airline.model.OptionMetadata;
import com.github.rvesse.airline.model.PositionalArgumentMetadata;
import com.github.rvesse.airline.parser.ParseState;
import com.github.rvesse.airline.parser.errors.ParseRestrictionViolatedException;
import com.github.rvesse.airline.restrictions.AbstractCommonRestriction;
Expand Down Expand Up @@ -69,6 +70,15 @@ public <T> void preValidate(ParseState<T> state, ArgumentsMetadata arguments, St
AbstractCommonRestriction.getArgumentTitle(state, arguments), value, this.pattern.toString(),
StringUtils.isNotBlank(this.description) ? this.description : "");
}

@Override
public <T> void preValidate(ParseState<T> state, PositionalArgumentMetadata arguments, String value) {
if (!this.pattern.matcher(value).find())
throw new ParseRestrictionViolatedException(
"Positional Argument %d ('%s') was given value '%s' which does not match the regular expression '%s'. %s",
arguments.getZeroBasedPosition(), arguments.getTitle(), value, this.pattern.toString(),
StringUtils.isNotBlank(this.description) ? this.description : "");
}

@Override
public String getPreamble() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import com.github.rvesse.airline.help.sections.HelpHint;
import com.github.rvesse.airline.model.ArgumentsMetadata;
import com.github.rvesse.airline.model.OptionMetadata;
import com.github.rvesse.airline.model.PositionalArgumentMetadata;
import com.github.rvesse.airline.parser.ParseState;
import com.github.rvesse.airline.parser.errors.ParseInvalidRestrictionException;
import com.github.rvesse.airline.parser.errors.ParseRestrictionViolatedException;
Expand Down Expand Up @@ -65,6 +66,10 @@ protected void invalidOptionPort(OptionMetadata option, Object value) {
protected void invalidArgumentsPort(ArgumentsMetadata arguments, String title, Object value) {
invalidPort(String.format("Argument '%s'", title), value);
}

protected void invalidArgumentsPort(PositionalArgumentMetadata arguments, String title, Object value) {
invalidPort(String.format("Positional argument %d ('%s')", arguments.getZeroBasedPosition(), title), value);
}

protected void invalidPort(String title, Object value) {
throw new ParseRestrictionViolatedException(
Expand Down Expand Up @@ -92,6 +97,27 @@ public <T> void postValidate(ParseState<T> state, ArgumentsMetadata arguments, O
arguments.getJavaType());
}
}

@Override
public <T> void postValidate(ParseState<T> state, PositionalArgumentMetadata arguments, Object value) {
if (acceptablePorts.isEmpty())
return;

String title = arguments.getTitle();
if (value instanceof Long) {
if (!isValid(((Long) value).longValue()))
invalidArgumentsPort(arguments, title, value);
} else if (value instanceof Integer) {
if (!isValid(((Integer) value).intValue()))
invalidArgumentsPort(arguments, title, value);
} else if (value instanceof Short) {
if (!isValid(((Short) value).shortValue()))
invalidArgumentsPort(arguments, title, value);
} else {
throw new ParseInvalidRestrictionException("Cannot apply a @Port restriction to an option of type %s",
arguments.getJavaType());
}
}

private boolean isValid(long port) {
if (port < MIN_PORT || port > MAX_PORT)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import com.github.rvesse.airline.help.sections.HelpHint;
import com.github.rvesse.airline.model.ArgumentsMetadata;
import com.github.rvesse.airline.model.OptionMetadata;
import com.github.rvesse.airline.model.PositionalArgumentMetadata;
import com.github.rvesse.airline.parser.ParseState;
import com.github.rvesse.airline.parser.errors.ParseRestrictionViolatedException;
import com.github.rvesse.airline.restrictions.AbstractCommonRestriction;
Expand Down Expand Up @@ -111,11 +112,20 @@ public <T> void postValidate(ParseState<T> state, OptionMetadata option, Object
@Override
public <T> void postValidate(ParseState<T> state, ArgumentsMetadata arguments, Object value) {
if (!this.isValid(value)) {
throw new ParseRestrictionViolatedException("Option '%s' must have a %s value (%s) but got %s",
throw new ParseRestrictionViolatedException("Argument '%s' must have a %s value (%s) but got %s",
AbstractCommonRestriction.getArgumentTitle(state, arguments), this.type, this.range, value);
}
}

@Override
public <T> void postValidate(ParseState<T> state, PositionalArgumentMetadata arguments, Object value) {
if (!this.isValid(value)) {
throw new ParseRestrictionViolatedException(
"Positional argument %d ('%s') must have a %s value (%s) but got %s",
arguments.getZeroBasedPosition(), arguments.getTitle(), this.type, this.range, value);
}
}

@Override
public String getPreamble() {
return null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import com.github.rvesse.airline.help.sections.HelpHint;
import com.github.rvesse.airline.model.ArgumentsMetadata;
import com.github.rvesse.airline.model.OptionMetadata;
import com.github.rvesse.airline.model.PositionalArgumentMetadata;
import com.github.rvesse.airline.parser.ParseState;
import com.github.rvesse.airline.parser.errors.ParseInvalidRestrictionException;
import com.github.rvesse.airline.parser.errors.ParseOptionOutOfRangeException;
Expand Down Expand Up @@ -83,6 +84,16 @@ public <T> void postValidate(ParseState<T> state, ArgumentsMetadata arguments, O
if (!inRange(value))
throw new ParseOptionOutOfRangeException(getArgumentTitle(state, arguments), value, min, minInclusive, max, maxInclusive);
}

@Override
public <T> void postValidate(ParseState<T> state, PositionalArgumentMetadata arguments, Object value) {
// Not enforced if no range provided
if (this.min == null && this.max == null)
return;

if (!inRange(value))
throw new ParseOptionOutOfRangeException(arguments.getTitle(), value, min, minInclusive, max, maxInclusive);
}

protected boolean inRange(Object value) {
if (this.min != null) {
Expand Down

0 comments on commit bfe802e

Please sign in to comment.