Skip to content

Commit

Permalink
Merge pull request #19 from swarren12/feature/more-convenience
Browse files Browse the repository at this point in the history
Add some more convenience methods to `DslValues`
  • Loading branch information
Palmr authored Aug 23, 2022
2 parents aa8d2dc + 944b488 commit 1e06e14
Show file tree
Hide file tree
Showing 5 changed files with 246 additions and 8 deletions.
2 changes: 1 addition & 1 deletion src/main/java/com/lmax/simpledsl/api/DslArg.java
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ public interface DslArg

/**
* Get a default value for this argument.
*
* <p>
* If the argument is required, this method will throw an {@link IllegalArgumentException}.
*
* @return the default value for the argument
Expand Down
75 changes: 74 additions & 1 deletion src/main/java/com/lmax/simpledsl/api/DslValues.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.OptionalDouble;
import java.util.OptionalInt;
import java.util.OptionalLong;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;
Expand Down Expand Up @@ -276,6 +279,34 @@ default String valueAsParam(final String name)
return value != null ? name + ": " + value : null;
}

/**
* Retrieve the value supplied for a parameter formatted as a parameter with the given name.
* For example, if the parameter {@literal user} was given the value {@literal jenny}, then
* {@code valueAsParamNamed("user", "person")} would return {@code person: jenny}.
* <p>
* This is useful when reusing DSL methods to build higher level functions. e.g.
*
* <pre>{@code
* public void createUserAndLogin(String... args) {
* DslParams params = new DslParams(args,
* new RequiredParam("user"),
* new RequiredParam("accountType"));
* generateRandomUser(params.valueAsParamNamed("user", "rememberUserAs"));
* login(params.valueAsParam("user"), "password: password");
* }
* }</pre>
*
* @param oldParamName the name of the parameter.
* @param newParamName the new name of the parameter.
* @return the value supplied for that parameter, formatted as a parameter ready to pass on to another method that uses Simple-DSL.
* @throws IllegalArgumentException if {@code name} does not match the name of a supported parameter or if the parameter supports multiple values.
*/
default String valueAsParamNamed(final String oldParamName, final String newParamName)
{
final String value = value(oldParamName);
return value != null ? newParamName + ": " + value : null;
}

/**
* Retrieve the values supplied for a parameter as a {@link List}. Returns an empty list if the parameter is optional and a value has not been supplied.
*
Expand Down Expand Up @@ -305,7 +336,7 @@ default <T> List<T> valuesAsListOf(final String name, final Function<String, T>
/**
* Retrieve the values supplied for a parameter as an {@link Optional} {@link List}.
* <p>
* Returns an empty Optional if the parameter is optional and a value has not been supplied.
* Returns an {@link Optional#empty() empty Optional} if the parameter is optional and a value has not been supplied.
* <p>
* In most cases {@link #valuesAsList} is the more suitable method.
* This variant is useful if there is an important difference between a parameter being set to an empty list vs not being supplied.
Expand All @@ -331,6 +362,48 @@ default Optional<List<String>> valuesAsOptional(final String name)
.filter(list -> !list.isEmpty());
}

/**
* Retrieve the values supplied for a parameter as an {@link OptionalInt}.
* <p>
* Returns an {@link OptionalInt#empty() empty Optional} if the parameter is optional and a value has not been supplied.
*
* @param name the name of the parameter.
* @return the value of the parameter, or empty
* @throws IllegalArgumentException if {@code name} does not match the name of a supported parameter.
*/
default OptionalInt valuesAsOptionalInt(final String name)
{
return hasValue(name) ? OptionalInt.of(valueAsInt(name)) : OptionalInt.empty();
}

/**
* Retrieve the values supplied for a parameter as an {@link OptionalLong}.
* <p>
* Returns an {@link OptionalLong#empty() empty Optional} if the parameter is optional and a value has not been supplied.
*
* @param name the name of the parameter.
* @return the value of the parameter, or empty
* @throws IllegalArgumentException if {@code name} does not match the name of a supported parameter.
*/
default OptionalLong valuesAsOptionalLong(final String name)
{
return hasValue(name) ? OptionalLong.of(valueAsLong(name)) : OptionalLong.empty();
}

/**
* Retrieve the values supplied for a parameter as an {@link OptionalDouble}.
* <p>
* Returns an {@link OptionalDouble#empty() empty Optional} if the parameter is optional and a value has not been supplied.
*
* @param name the name of the parameter.
* @return the value of the parameter, or empty
* @throws IllegalArgumentException if {@code name} does not match the name of a supported parameter.
*/
default OptionalDouble valuesAsOptionalDouble(final String name)
{
return hasValue(name) ? OptionalDouble.of(valueAsInt(name)) : OptionalDouble.empty();
}

/**
* Retrieve the values supplied for a parameter as an {@code int} array.
* <p>
Expand Down
34 changes: 33 additions & 1 deletion src/main/java/com/lmax/simpledsl/api/SimpleDslArg.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@

package com.lmax.simpledsl.api;

import static java.util.Arrays.stream;

/**
* The root type for all simple args.
*/
Expand Down Expand Up @@ -96,7 +98,8 @@ public SimpleDslArg setDefault(final String defaultValue)

/**
* Restrict the allowed values for this argument to the specified set.
* Specifying a value outside of this set will result in an exception being thrown when parsing the arguments.
* <p>
* Specifying a value outside this set will result in an exception being thrown when parsing the arguments.
*
* @param allowedValues the allowable values for this argument.
* @return this argument
Expand All @@ -107,6 +110,35 @@ public SimpleDslArg setAllowedValues(final String... allowedValues)
return this;
}

/**
* Restrict the allowed values for this argument to the specified set.
* <p>
* Specifying a value outside this set will result in an exception being thrown when parsing the arguments.
*
* @param <T> the type
* @param clazz the {@link Class} that provides the allowed values.
* @return this argument
* @throws IllegalArgumentException if allowed values cannot be determined from the provided class
*/
public <T> SimpleDslArg setAllowedValues(final Class<T> clazz)
{
if (Boolean.class.isAssignableFrom(clazz))
{
return setAllowedValues("true", "false");
}
else if (Enum.class.isAssignableFrom(clazz))
{
return setAllowedValues(
stream(clazz.getEnumConstants())
.map(constant -> (Enum<?>) constant)
.map(Enum::name)
.toArray(String[]::new)
);
}

throw new IllegalArgumentException("Cannot assign allowed values from class " + clazz.getName());
}

/**
* Allow multiple values to be specified for this argument, either as separate arguments or using comma (,) as a delimiter.
* <p>
Expand Down
61 changes: 57 additions & 4 deletions src/test/java/com/lmax/simpledsl/internal/DslParamsImplTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.OptionalDouble;
import java.util.OptionalInt;
import java.util.OptionalLong;

import static java.util.Arrays.asList;
import static java.util.Collections.emptyList;
Expand Down Expand Up @@ -74,7 +77,7 @@ public void shouldReturnMultipleValues()

final DslParams params = new DslParamsImpl(new DslArg[0], Collections.singletonMap("a", aParam));

assertArrayEquals(new String[] {"Hello World", "Goodbye, Cruel World"}, params.values("a"));
assertArrayEquals(new String[]{"Hello World", "Goodbye, Cruel World"}, params.values("a"));
}

@Test
Expand All @@ -85,7 +88,7 @@ public void shouldReturnMultipleValuesWhenUsingAMappingFunctionToGenericArray()
final DslParams params = new DslParamsImpl(new DslArg[0], Collections.singletonMap("a", aParam));

assertArrayEquals(
new Object[] {TestValues.VALUE_1, TestValues.VALUE_2},
new Object[]{TestValues.VALUE_1, TestValues.VALUE_2},
params.valuesAs("a", TestValues::valueOf)
);
}
Expand All @@ -98,7 +101,7 @@ public void shouldReturnMultipleValuesWhenUsingAMappingFunctionToArrayOfSpecific
final DslParams params = new DslParamsImpl(new DslArg[0], Collections.singletonMap("a", aParam));

assertArrayEquals(
new TestValues[] {TestValues.VALUE_1, TestValues.VALUE_2},
new TestValues[]{TestValues.VALUE_1, TestValues.VALUE_2},
params.valuesAs("a", TestValues.class, TestValues::valueOf)
);
}
Expand All @@ -111,7 +114,7 @@ public void shouldReturnMultipleValuesAsAnEnum()
final DslParams params = new DslParamsImpl(new DslArg[0], Collections.singletonMap("a", aParam));

assertArrayEquals(
new TestValues[] {TestValues.VALUE_1, TestValues.VALUE_2},
new TestValues[]{TestValues.VALUE_1, TestValues.VALUE_2},
params.valuesAs("a", TestValues.class)
);
}
Expand Down Expand Up @@ -244,6 +247,26 @@ public void shouldReturnNullValueAsParamWhenSimpleDslParamNotSpecified()
assertNull(params.valueAsParam("a"));
}

@Test
public void shouldReturnValueAsNamedParamForOptionalValueThatWasSpecified()
{
final SimpleDslParam aParam = new SimpleDslParam("a", singletonList("value"));

final DslParams params = new DslParamsImpl(new DslArg[0], Collections.singletonMap("a", aParam));

assertEquals("b: value", params.valueAsParamNamed("a", "b"));
}

@Test
public void shouldReturnNullValueAsNamedParamWhenSimpleDslParamNotSpecified()
{
final SimpleDslParam aParam = new SimpleDslParam("a", emptyList());

final DslParams params = new DslParamsImpl(new DslArg[0], Collections.singletonMap("a", aParam));

assertNull(params.valueAsParamNamed("a", "b"));
}

@Test
public void shouldReturnValuesAsIntArray()
{
Expand Down Expand Up @@ -447,6 +470,36 @@ public void shouldReturnEmptyOptionalListWhenNoValuesAreSupplied()
assertEquals(Optional.empty(), params.valuesAsOptional("a"));
}

@Test
public void shouldReturnEmptyOptionalIntWhenNoValuesAreSupplied()
{
final SimpleDslParam aParam = new SimpleDslParam("a", emptyList());

final DslParams params = new DslParamsImpl(new DslArg[0], Collections.singletonMap("a", aParam));

assertEquals(OptionalInt.empty(), params.valuesAsOptionalInt("a"));
}

@Test
public void shouldReturnEmptyOptionalLongWhenNoValuesAreSupplied()
{
final SimpleDslParam aParam = new SimpleDslParam("a", emptyList());

final DslParams params = new DslParamsImpl(new DslArg[0], Collections.singletonMap("a", aParam));

assertEquals(OptionalLong.empty(), params.valuesAsOptionalLong("a"));
}

@Test
public void shouldReturnEmptyOptionalDoubleWhenNoValuesAreSupplied()
{
final SimpleDslParam aParam = new SimpleDslParam("a", emptyList());

final DslParams params = new DslParamsImpl(new DslArg[0], Collections.singletonMap("a", aParam));

assertEquals(OptionalDouble.empty(), params.valuesAsOptionalDouble("a"));
}

@Test
public void shouldReturnOptionalListWhenMultipleParameterValueIsSupplied()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -626,7 +626,79 @@ public void shouldMatchAllowedValuesCaseInsensitivelyAndReturnValuesUsingTheCase
}

@Test
public void shouldThrowAnExceptionIfRequiredArgeterMissingFromGroup()
public void shouldMatchAllowedValuesSpecifiedViaABoolean()
{
final String[] args = {
"thisWorks: true",
};
final DslArg[] parameters = {
new RequiredArg("thisWorks").setAllowedValues(Boolean.class),
};

final DslParamsParser parser = new DslParamsParser();

final DslParams params = parser.parse(args, parameters);

assertTrue(params.valueAsBoolean("thisWorks"));
}

@Test
public void shouldThrowAnExceptionIfValuesDoesNotMatchAllowedValuesSpecifiedViaABoolean()
{
final String[] args = {
"thisWorks: NO",
};
final DslArg[] parameters = {
new RequiredArg("thisWorks").setAllowedValues(Boolean.class),
};

final DslParamsParser parser = new DslParamsParser();

final IllegalArgumentException exception = assertThrows(
IllegalArgumentException.class,
() -> parser.parse(args, parameters));

assertEquals("thisWorks parameter value 'NO' must be one of: [true, false]", exception.getMessage());
}

@Test
public void shouldMatchAllowedValuesSpecifiedViaAnEnum()
{
final String[] args = {
"pet: DRAGON",
};
final DslArg[] parameters = {
new RequiredArg("pet").setAllowedValues(PossiblePets.class),
};

final DslParamsParser parser = new DslParamsParser();

final DslParams params = parser.parse(args, parameters);

assertEquals("DRAGON", params.value("pet"));
}

@Test
public void shouldThrowAnExceptionIfValuesDoesNotMatchAllowedValuesSpecifiedViaAnEnum()
{
final String[] args = {
"pet: UNICORN",
};
final DslArg[] parameters = {
new RequiredArg("pet").setAllowedValues(PossiblePets.class),
};

final DslParamsParser parser = new DslParamsParser();

final IllegalArgumentException exception = assertThrows(
IllegalArgumentException.class,
() -> parser.parse(args, parameters));

assertEquals("pet parameter value 'UNICORN' must be one of: [COW, SHEEP, GOAT, DRAGON]", exception.getMessage());
}

@Test
public void shouldThrowAnExceptionIfRequiredParameterMissingFromGroup()
{
final String[] args = {"a: value", "myGroup: Joe", "myGroup: Jenny", "myValue: 2"};
final DslArg[] params = {
Expand Down Expand Up @@ -805,4 +877,12 @@ public void maybeShouldThrowExceptionIfRequiredParametersArePassedOutOfOrder()

assertEquals("Unexpected ambiguous argument 1", exception.getMessage());
}

private enum PossiblePets
{
COW,
SHEEP,
GOAT,
DRAGON
}
}

0 comments on commit 1e06e14

Please sign in to comment.