Compare commits

..

No commits in common. "58796c8f73cef2f02309baef6084347748e1599e" and "612fd7138a712a3bad3164a3f8c23e9e5d950482" have entirely different histories.

16 changed files with 275 additions and 405 deletions

View file

@ -15,7 +15,7 @@ jobs:
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- name: setup-jdk-${{ matrix.java }} - name: setup-jdk-${{ matrix.java }}
uses: actions/setup-java@v2.2.0 uses: actions/setup-java@v2.1.0
with: with:
distribution: 'adopt' distribution: 'adopt'
java-version: ${{ matrix.java }} java-version: ${{ matrix.java }}

View file

@ -12,7 +12,7 @@ jobs:
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- name: Set up JDK - name: Set up JDK
uses: actions/setup-java@v2.2.0 uses: actions/setup-java@v2.1.0
with: with:
distribution: 'adopt' distribution: 'adopt'
java-version: 11 java-version: 11

View file

@ -14,8 +14,6 @@ https://oss.sonatype.org/content/repositories/releases/net/kemitix/mon/)
https://img.shields.io/maven-central/v/net.kemitix/mon.svg?style=for-the-badge)]( https://img.shields.io/maven-central/v/net.kemitix/mon.svg?style=for-the-badge)](
https://search.maven.org/artifact/net.kemitix/mon) https://search.maven.org/artifact/net.kemitix/mon)
The documentation below is being slowly migrated to [Javadoc](https://kemitix.github.io/mon/).
- [Maven Usage](#Maven-Usage) - [Maven Usage](#Maven-Usage)
- [Wrapper](#Wrapper) - light-weight type-alias-like - [Wrapper](#Wrapper) - light-weight type-alias-like
- [TypeAlias](#TypeAlias) - type-alias-like monadic wrapper - [TypeAlias](#TypeAlias) - type-alias-like monadic wrapper

View file

@ -11,7 +11,7 @@
</parent> </parent>
<artifactId>mon</artifactId> <artifactId>mon</artifactId>
<version>3.3.0</version> <version>3.0.0</version>
<name>Mon</name> <name>Mon</name>
<description>Wrapper, TypeAlias, Maybe, Result, Tree, Lazy, Either and Combinators for Java. <description>Wrapper, TypeAlias, Maybe, Result, Tree, Lazy, Either and Combinators for Java.
@ -41,9 +41,9 @@
<kemitix-maven-tiles.version>3.0.1</kemitix-maven-tiles.version> <kemitix-maven-tiles.version>3.0.1</kemitix-maven-tiles.version>
<digraph-dependency.basePackage>net.kemitix.mon</digraph-dependency.basePackage> <digraph-dependency.basePackage>net.kemitix.mon</digraph-dependency.basePackage>
<kemitix-checkstyle.version>5.5.0</kemitix-checkstyle.version> <kemitix-checkstyle.version>5.5.0</kemitix-checkstyle.version>
<pitest-maven-plugin.version>1.6.9</pitest-maven-plugin.version> <pitest-maven-plugin.version>1.6.8</pitest-maven-plugin.version>
<pitest-junit5-plugin.version>0.14</pitest-junit5-plugin.version> <pitest-junit5-plugin.version>0.14</pitest-junit5-plugin.version>
<spotbugs.version>4.4.0</spotbugs.version> <spotbugs.version>4.3.0</spotbugs.version>
<apiguardian-api.version>1.1.2</apiguardian-api.version> <apiguardian-api.version>1.1.2</apiguardian-api.version>
</properties> </properties>
@ -60,6 +60,7 @@
<artifactId>apiguardian-api</artifactId> <artifactId>apiguardian-api</artifactId>
<version>${apiguardian-api.version}</version> <version>${apiguardian-api.version}</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.junit.jupiter</groupId> <groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId> <artifactId>junit-jupiter-api</artifactId>

View file

@ -1,64 +0,0 @@
package net.kemitix.mon.reader;
import org.apiguardian.api.API;
import java.util.function.BiFunction;
import java.util.function.Function;
/**
* Returns a program ready to run upon the supply of a suitable environment.
*
* @param <E> The type of the environment required by the program.
* @param <R> The type of the result returned by the program.
*/
@API(status = API.Status.EXPERIMENTAL, since = "3.0.1")
@FunctionalInterface
public interface Reader<E, R> {
/**
* Executes the program.
*
* @param env the required environment
* @return the result of the program
*/
R run(E env);
/**
* Applies the function provided to the reader when it is run.
*
* @param f the function, which takes an {@link E} as its only parameter
* @param <V> the type of the functions output
* @return a new Reader to provide the result of the supplied function
*/
@API(status = API.Status.EXPERIMENTAL)
default <V> Reader<E, V> map(Function<R, V> f) {
return e -> f.apply(run(e));
}
/**
* Applies the function provided to the reader when it is run.
*
* @param f the function, which takes an {@link E} and the previously
* generated value as its two parameters
* @param <V> the type of the functions output
* @return a new Reader to provided the result of the supplied function
*/
@API(status = API.Status.EXPERIMENTAL)
default <V> Reader<E, V> flatMap(BiFunction<E, R, V> f) {
return env -> f.apply(env, run(env));
}
/**
* Applies the function provided to the reader when run, and executes that
* resulting reader.
*
* @param f the function which takes the previously generated value and
* produces another Reader
* @param <V> the type of the value the new Reader returns
* @return a new Reader
*/
@API(status = API.Status.EXPERIMENTAL)
default <V> Reader<E, V> flatMap(Function<R, Reader<E, V>> f) {
return env -> f.apply(run(env)).run(env);
}
}

View file

@ -1 +0,0 @@
package net.kemitix.mon.reader;

View file

@ -24,6 +24,7 @@ package net.kemitix.mon.result;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import java.util.Objects; import java.util.Objects;
import java.util.concurrent.Callable;
import java.util.function.BinaryOperator; import java.util.function.BinaryOperator;
import java.util.function.Consumer; import java.util.function.Consumer;
import java.util.function.Function; import java.util.function.Function;
@ -66,9 +67,8 @@ class Err<T> implements Result<T> {
} }
@Override @Override
public Result<T> match(final Consumer<T> onSuccess, final Consumer<Throwable> onError) { public void match(final Consumer<T> onSuccess, final Consumer<Throwable> onError) {
onError.accept(error); onError.accept(error);
return this;
} }
@Override @Override
@ -101,8 +101,8 @@ class Err<T> implements Result<T> {
} }
@Override @Override
public Result<T> onSuccess(final Consumer<T> successConsumer) { public void onSuccess(final Consumer<T> successConsumer) {
return this; // do nothing
} }
@Override @Override
@ -122,6 +122,11 @@ class Err<T> implements Result<T> {
return this; return this;
} }
@Override
public <R> Result<R> andThen(final Function<T, Callable<R>> f) {
return (Result<R>) this;
}
@Override @Override
public Result<T> thenWith(final Function<T, WithResultContinuation<T>> f) { public Result<T> thenWith(final Function<T, WithResultContinuation<T>> f) {
return this; return this;

View file

@ -26,12 +26,11 @@ public class ErrVoid implements ResultVoid {
} }
@Override @Override
public ResultVoid match( public void match(
final Runnable onSuccess, final Runnable onSuccess,
final Consumer<Throwable> onError final Consumer<Throwable> onError
) { ) {
onError.accept(error); onError.accept(error);
return this;
} }
@Override @Override
@ -40,8 +39,8 @@ public class ErrVoid implements ResultVoid {
} }
@Override @Override
public ResultVoid onSuccess(final Runnable runnable) { public void onSuccess(final Runnable runnable) {
return this; // do nothing
} }
@Override @Override

View file

@ -446,6 +446,23 @@ public interface Result<T> extends BaseResult, ThrowableFunctor<T, ThrowableFunc
return maybeResult.flatMap(f); return maybeResult.flatMap(f);
} }
/**
* Swaps the inner {@code Result} of a {@link Maybe}, so that a {@code Result} contains a {@code Maybe}.
*
* @param maybeResult the Maybe the contains a Result
* @param <T> the type of the value that may be in the Result
* @return a Result containing a Maybe, the value in the Maybe was the value in a successful Result within the
* original Maybe. If the original Maybe is Nothing, the Result will contain Nothing. If the original Result was an
* error, then the Result will also be an error.
* @deprecated
*/
@API(status = DEPRECATED)
@Deprecated
static <T> Result<Maybe<T>> swap(final Maybe<Result<T>> maybeResult) {
return maybeResult.orElseGet(() -> Result.ok(null))
.flatMap(value -> Result.ok(Maybe.maybe(value)));
}
/** /**
* Creates a {@link Maybe} from the {@code Result}. * Creates a {@link Maybe} from the {@code Result}.
* *
@ -640,10 +657,9 @@ public interface Result<T> extends BaseResult, ThrowableFunctor<T, ThrowableFunc
* *
* @param onSuccess the Consumer to pass the value of a successful Result to * @param onSuccess the Consumer to pass the value of a successful Result to
* @param onError the Consumer to pass the error from an error Result to * @param onError the Consumer to pass the error from an error Result to
* @return the original Result
*/ */
@API(status = STABLE) @API(status = STABLE)
Result<T> match(Consumer<T> onSuccess, Consumer<Throwable> onError); void match(Consumer<T> onSuccess, Consumer<Throwable> onError);
/** /**
* Provide the value within the Result, if it is a success, to the Consumer, and returns this Result. * Provide the value within the Result, if it is a success, to the Consumer, and returns this Result.
@ -691,10 +707,9 @@ public interface Result<T> extends BaseResult, ThrowableFunctor<T, ThrowableFunc
* success value. When this is an error, then nothing happens.</p> * success value. When this is an error, then nothing happens.</p>
* *
* @param successConsumer the consumer to handle the success * @param successConsumer the consumer to handle the success
* @return the original Result
*/ */
@API(status = STABLE) @API(status = STABLE)
Result<T> onSuccess(Consumer<T> successConsumer); void onSuccess(Consumer<T> successConsumer);
/** /**
* A handler for error state, when the error matches the errorClass. * A handler for error state, when the error matches the errorClass.
@ -723,6 +738,29 @@ public interface Result<T> extends BaseResult, ThrowableFunctor<T, ThrowableFunc
Consumer<E> consumer Consumer<E> consumer
); );
/**
* Maps a Success Result to another Result using a Callable that is able to throw a checked exception.
*
* <p>Combination of {@link #flatMap(Function)} and {@link #of(Callable)}.</p>
*
* <pre><code>
* Integer doSomething() {...}
* String doSomethingElse(final Integer value) {...}
* Result&lt;String&gt; r = Result.of(() -&gt; doSomething())
* .andThen(value -&gt; () -&gt; doSomethingElse(value));
* </code></pre>
*
* <p>When the Result is an Err, then the original error is carried over and the Callable is never called.</p>
*
* @param f the function to map the Success value into the Callable
* @param <R> the type of the final Result
* @return a new Result
* @deprecated Use {@link #map(ThrowableFunction)}
*/
@API(status = DEPRECATED)
@Deprecated
<R> Result<R> andThen(Function<T, Callable<R>> f);
/** /**
* Perform the continuation with the value within the success {@code Result} * Perform the continuation with the value within the success {@code Result}
* and return itself. * and return itself.

View file

@ -27,10 +27,9 @@ public interface ResultVoid extends BaseResult {
* *
* @param onSuccess the Consumer to pass the value of a successful Result to * @param onSuccess the Consumer to pass the value of a successful Result to
* @param onError the Consumer to pass the error from an error Result to * @param onError the Consumer to pass the error from an error Result to
* @return the original ResultVoid
*/ */
@API(status = STABLE) @API(status = STABLE)
ResultVoid match(Runnable onSuccess, Consumer<Throwable> onError); void match(Runnable onSuccess, Consumer<Throwable> onError);
/** /**
* Attempts to restore an error {@code ResultVoid} to a success. * Attempts to restore an error {@code ResultVoid} to a success.
@ -67,10 +66,9 @@ public interface ResultVoid extends BaseResult {
* success value. When this is an error, then nothing happens.</p> * success value. When this is an error, then nothing happens.</p>
* *
* @param runnable the call if the Result is a success * @param runnable the call if the Result is a success
* @return the original ResultVoid
*/ */
@API(status = STABLE) @API(status = STABLE)
ResultVoid onSuccess(Runnable runnable); void onSuccess(Runnable runnable);
/** /**
* A handler for error state, when the error matches the errorClass. * A handler for error state, when the error matches the errorClass.

View file

@ -24,6 +24,7 @@ package net.kemitix.mon.result;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import java.util.Objects; import java.util.Objects;
import java.util.concurrent.Callable;
import java.util.function.BinaryOperator; import java.util.function.BinaryOperator;
import java.util.function.Consumer; import java.util.function.Consumer;
import java.util.function.Function; import java.util.function.Function;
@ -61,9 +62,8 @@ class Success<T> implements Result<T> {
} }
@Override @Override
public Result<T> match(final Consumer<T> onSuccess, final Consumer<Throwable> onError) { public void match(final Consumer<T> onSuccess, final Consumer<Throwable> onError) {
onSuccess.accept(value); onSuccess.accept(value);
return this;
} }
@Override @Override
@ -93,9 +93,8 @@ class Success<T> implements Result<T> {
} }
@Override @Override
public Result<T> onSuccess(final Consumer<T> successConsumer) { public void onSuccess(final Consumer<T> successConsumer) {
successConsumer.accept(value); successConsumer.accept(value);
return this;
} }
@Override @Override
@ -111,6 +110,11 @@ class Success<T> implements Result<T> {
return this; return this;
} }
@Override
public <R> Result<R> andThen(final Function<T, Callable<R>> f) {
return result(f.apply(value));
}
@Override @Override
public Result<T> thenWith(final Function<T, WithResultContinuation<T>> f) { public Result<T> thenWith(final Function<T, WithResultContinuation<T>> f) {
return f.apply(value).call(this); return f.apply(value).call(this);

View file

@ -37,9 +37,8 @@ public class SuccessVoid implements ResultVoid {
} }
@Override @Override
public ResultVoid match(final Runnable onSuccess, final Consumer<Throwable> onError) { public void match(final Runnable onSuccess, final Consumer<Throwable> onError) {
onSuccess.run(); onSuccess.run();
return this;
} }
@Override @Override
@ -48,9 +47,8 @@ public class SuccessVoid implements ResultVoid {
} }
@Override @Override
public ResultVoid onSuccess(final Runnable runnable) { public void onSuccess(final Runnable runnable) {
runnable.run(); runnable.run();
return this;
} }
@Override @Override

View file

@ -209,7 +209,7 @@ class EitherTest implements WithAssertions {
} }
@Nested @DisplayName("getLeft") public class GetLeft { @Nested @DisplayName("getLeft") public class GetLeft {
@Test @Test
@DisplayName("when is a Left then get the value") @DisplayName("")
public void whenLeft_thenGetValue() { public void whenLeft_thenGetValue() {
//given //given
Either<String, Integer> either = Either.left("value"); Either<String, Integer> either = Either.left("value");
@ -220,7 +220,7 @@ class EitherTest implements WithAssertions {
} }
@Test @Test
@DisplayName("when is a Right then is empty") @DisplayName("")
public void whenRight_thenGetEmpty() { public void whenRight_thenGetEmpty() {
//given //given
Either<Integer, String> either = Either.right("value"); Either<Integer, String> either = Either.right("value");
@ -232,7 +232,7 @@ class EitherTest implements WithAssertions {
} }
@Nested @DisplayName("getRight") public class GetRight { @Nested @DisplayName("getRight") public class GetRight {
@Test @Test
@DisplayName("when is a Left then is empty") @DisplayName("")
public void whenLeft_thenGetEmpty() { public void whenLeft_thenGetEmpty() {
//given //given
Either<String, Integer> either = Either.left("value"); Either<String, Integer> either = Either.left("value");
@ -243,7 +243,7 @@ class EitherTest implements WithAssertions {
} }
@Test @Test
@DisplayName("when is a Right then get the value") @DisplayName("")
public void whenRight_thenGetValue() { public void whenRight_thenGetValue() {
//given //given
Either<Integer, String> either = Either.right("value"); Either<Integer, String> either = Either.right("value");

View file

@ -1,110 +0,0 @@
package net.kemitix.mon;
import net.kemitix.mon.reader.Reader;
import org.assertj.core.api.WithAssertions;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Function;
public class ReaderTest
implements WithAssertions {
@Test
@DisplayName("read a value and return it")
void readAndReturn() {
//given
Integer value = 123;
Environment env = new Environment() {
@Override
public Integer intValue() {
return value;
}
};
Reader<Environment, Integer> program = (Environment e) -> {
return e.intValue();
};
//when
Integer result = program.run(env);
//then
assertThat(result).isEqualTo(value);
}
@Test
@DisplayName("map")
void map() {
//given
AtomicInteger value = new AtomicInteger(123);
Environment env = new Environment() {
@Override
public Integer intValue() {
return value.incrementAndGet();
}
};
Reader<Environment, String> program =
((Reader<Environment, Integer>) e -> e.intValue())
.map(i -> i * 2)
.map(i -> Integer.toString(i));
//when
String result = program.run(env);
//then
assertThat(result).isEqualTo("248");// (123 + 1) * 2
}
@Test
@DisplayName("andThen")
void andThen() {
//given
AtomicInteger value = new AtomicInteger(123);
Environment env = new Environment() {
@Override
public Integer intValue() {
return value.incrementAndGet();
}
};
Reader<Environment, String> program =
((Reader<Environment, Integer>) e -> e.intValue())
.flatMap((e, i) -> e.intValue() + (i * 2))
.flatMap((e, i) -> String.format("%.2f",
i.floatValue() / e.intValue()));
//when
String result = program.run(env);
//then
assertThat(result).isEqualTo("2.96");// ((123 * 2) + (123 + 1)) / (123 + 2)
}
@Test
@DisplayName("flatMap")
void flatMap() {
//given
AtomicInteger value = new AtomicInteger(123);
Environment env = new Environment() {
@Override
public Integer intValue() {
return value.incrementAndGet();
}
};
Function<Integer, Reader<Environment, Integer>> addNextValue =
integer -> (Reader<Environment, Integer>) e -> e.intValue() + integer;
Function<Integer, Reader<Environment, Float>> divideByNextValue =
integer -> (Reader<Environment, Float>) e -> integer.floatValue() / e.intValue();
Reader<Environment, String> program =
((Reader<Environment, Integer>) e -> e.intValue())
.flatMap(addNextValue)
.flatMap(divideByNextValue)
.map(f -> String.format("%.3f", f));
//when
String result = program.run(env);
//then
assertThat(result).isEqualTo("1.976");// (123 + 124) / 125
}
private interface Environment {
Integer intValue();
}
}

View file

@ -106,6 +106,17 @@ class ResultTest implements WithAssertions {
assertThat(result.isError()).isFalse(); assertThat(result.isError()).isFalse();
} }
@Test
void whenOkayVoid_match_isNull() {
//when
var result = Result.ok();
//then
result.match(
() -> assertThat(true).isTrue(),
error -> fail("not an error")
);
}
@Test @Test
void whenOk_isOkay() { void whenOk_isOkay() {
//when //when
@ -122,6 +133,17 @@ class ResultTest implements WithAssertions {
assertThat(result.isError()).isFalse(); assertThat(result.isError()).isFalse();
} }
@Test
void whenOkay_matchSuccess() {
//given
final Result<String> result = Result.ok("good");
//then
result.match(
success -> assertThat(success).isEqualTo("good"),
error -> fail("not an error")
);
}
@Test @Test
void whenError_isError() { void whenError_isError() {
//when //when
@ -138,6 +160,17 @@ class ResultTest implements WithAssertions {
assertThat(result.isError()).isTrue(); assertThat(result.isError()).isTrue();
} }
@Test
void whenError_matchError() {
//given
final Result<Integer> result = anError(new Exception("bad"));
//then
result.match(
success -> fail("not a success"),
error -> assertThat(error.getMessage()).isEqualTo("bad")
);
}
@Test @Test
void okay_toString() { void okay_toString() {
//given //given
@ -212,95 +245,6 @@ class ResultTest implements WithAssertions {
.flatMap(s -> Result.of(() -> {throw e;})); .flatMap(s -> Result.of(() -> {throw e;}));
} }
@Nested
@DisplayName("match")
class MatchTests {
@Test
void whenOkay_matchSuccess() {
//given
Result<String> ok = Result.ok("good");
//then
ok.match(
success -> assertThat(success).isEqualTo("good"),
error -> fail("not an error")
);
}
@Test
void whenError_matchError() {
//given
Result<Integer> error = anError(new Exception("bad"));
//then
error.match(
success -> fail("not a success"),
e -> assertThat(e.getMessage()).isEqualTo("bad")
);
}
@Test
void whenOkayVoid_matchOkay() {
//when
var ok = Result.ok();
//then
ok.match(
() -> assertThat(true).isTrue(),
error -> fail("not an error")
);
}
@Test
void whenErrorVoid_matchError() {
//when
var error = Result.error(new RuntimeException());
//then
error.match(
() -> fail("not a success"),
e -> assertThat(true).isTrue()
);
}
@Test
void whenOkay_match_returnsSelf() {
//given
final Result<String> ok = Result.ok("good");
//when
Result<String> result = ok.match(s -> {}, e -> {});
//then
assertThat(result).isSameAs(ok);
}
@Test
void whenError_match_returnsSelf() {
//given
final Result<Integer> error = anError(new Exception("bad"));
//then
Result<Integer> result = error.match(s -> {}, e -> {});
//then
assertThat(result).isSameAs(error);
}
@Test
void whenOkayVoid_match_returnsSelf() {
//given
ResultVoid ok = Result.ok();
//when
ResultVoid result = ok.match(() -> {}, e -> {});
//then
assertThat(result).isSameAs(ok);
}
@Test
void whenErrorVoid_match_returnsSelf() {
//given
ResultVoid error = Result.error(new RuntimeException());
//when
ResultVoid result = error.match(() -> {}, e -> {});
//then
assertThat(result).isSameAs(error);
}
}
@Nested @Nested
@DisplayName("flatMap") @DisplayName("flatMap")
class FlatMapTests { class FlatMapTests {
@ -583,6 +527,50 @@ class ResultTest implements WithAssertions {
} }
} }
@Nested
@DisplayName("invert")
class InvertTests {
@Test
void justOkay_whenInvert_thenOkayJust() {
//given
final Maybe<Result<Integer>> justSuccess = Maybe.just(Result.ok(1));
//when
final Result<Maybe<Integer>> result = Result.swap(justSuccess);
//then
result.match(
success -> assertThat(success.toOptional()).contains(1),
error -> fail("Not an error")
);
}
@Test
void JustError_whenInvert_isError() {
//given
final RuntimeException exception = new RuntimeException();
final Maybe<Result<Integer>> justError = Maybe.just(anError(exception));
//when
final Result<Maybe<Integer>> result = Result.swap(justError);
//then
result.match(
success -> fail("Not a success"),
error -> assertThat(error).isSameAs(exception)
);
}
@Test
void nothing_whenInvert_thenOkayNothing() {
//given
final Maybe<Result<Integer>> nothing = Maybe.nothing();
//when
final Result<Maybe<Integer>> result = Result.swap(nothing);
//then
result.match(
success -> assertThat(success.toOptional()).isEmpty(),
error -> fail("Not an error")
);
}
}
@Nested @Nested
@DisplayName("use cases") @DisplayName("use cases")
class UseCaseTests { class UseCaseTests {
@ -844,52 +832,6 @@ class ResultTest implements WithAssertions {
//then //then
assertThat(capture).hasValue(1); assertThat(capture).hasValue(1);
} }
@Test
void error_whenOnSuccess_returnsSelf() {
//given
final Result<Integer> error = anError(new RuntimeException());
//when
final Result<Integer> result = error.onSuccess(x -> {});
//then
assertThat(result).isSameAs(error);
}
@Test
void success_whenOnSuccess_returnsSelf() {
//given
final Result<Integer> ok = Result.ok(1);
//when
final Result<Integer> result = ok.onSuccess(x -> {});
//then
assertThat(result).isSameAs(ok);
}
@Test void errorVoid_whenOnSuccess_returnsSelf() {
//given
final ResultVoid error = Result.error(new RuntimeException());
//when
final ResultVoid result = error.onSuccess(() -> {});
//then
assertThat(result).isSameAs(error);
}
@Test void successVoid_whenOnSuccess_returnsSelf() {
//given
final ResultVoid ok = Result.ok();
//when
final ResultVoid result = ok.onSuccess(() -> {});
//then
assertThat(result).isSameAs(ok);
}
} }
@Nested @Nested
@ -1015,6 +957,122 @@ class ResultTest implements WithAssertions {
} }
@Nested
@DisplayName("andThen")
class AndThenTests {
@Test
void okay_whenAndThen_whereSuccess_isUpdatedSuccess() {
//given
final Result<Integer> ok = Result.ok(1);
//when
final Result<String> result = ok.andThen(v -> () -> "success");
//then
result.match(
v -> assertThat(v).isEqualTo("success"),
e -> fail("not an error"));
}
@Test
void okay_whenAndThen_whereError_isError() {
//given
final Result<Integer> ok = Result.ok(1);
final RuntimeException exception = new RuntimeException();
//when
final Result<Object> result = ok.andThen(v -> () -> {
throw exception;
});
//then
result.match(
x -> fail("not a success"),
e -> assertThat(e).isSameAs(exception));
}
@Test
void error_whereAndThen_whereSuccess_isError() {
//given
final RuntimeException exception = new RuntimeException();
final Result<Integer> error = anError(exception);
//when
final Result<Object> result = error.andThen(v -> () -> "success");
//then
result.match(
x -> fail("not a success"),
e -> assertThat(e).isSameAs(exception));
}
@Test
void error_whenAndThen_whereError_isOriginalError() {
//given
final RuntimeException exception1 = new RuntimeException();
final Result<Integer> error = anError(exception1);
//when
final Result<Object> result = error.andThen(v -> () -> {
throw new RuntimeException();
});
//then
result.match(
x -> fail("not a success"),
e -> assertThat(e).isSameAs(exception1));
}
@Test
void okayVoid_whenAndThen_whereSuccess_isUpdatedSuccess() {
//given
final ResultVoid ok = Result.ok();
//when
final ResultVoid result = ok.andThen(() -> {
// do nothing
});
//then
assertThat(result.isOkay()).isTrue();
}
@Test
void okayVoid_whenAndThen_whereError_isError() {
//given
final ResultVoid ok = Result.ok();
final RuntimeException exception = new RuntimeException();
//when
final ResultVoid result = ok.andThen(() -> {
throw exception;
});
//then
result.match(
() -> fail("not a success"),
e -> assertThat(e).isSameAs(exception));
}
@Test
void errorVoid_whereAndThen_whereSuccess_isError() {
//given
final RuntimeException exception = new RuntimeException();
final ResultVoid error = Result.error(exception);
//when
final ResultVoid result = error.andThen(() -> {
// do nothing
});
//then
result.match(
() -> fail("not a success"),
e -> assertThat(e).isSameAs(exception));
}
@Test
void errorVoid_whenAndThen_whereError_isOriginalError() {
//given
final RuntimeException exception1 = new RuntimeException();
final ResultVoid error = Result.error(exception1);
//when
final ResultVoid result = error.andThen(() -> {
throw new RuntimeException();
});
//then
result.match(
() -> fail("not a success"),
e -> assertThat(e).isSameAs(exception1));
}
}
@Nested @Nested
@DisplayName("thenWith") @DisplayName("thenWith")
class ThenWithTests { class ThenWithTests {
@ -1610,64 +1668,6 @@ class ResultTest implements WithAssertions {
} }
@Nested
@DisplayName("andThen")
class AndThenTests {
@Test
void successVoid_andThen_returnSelf() {
//given
ResultVoid ok = Result.ok();
//when
ResultVoid result = ok.andThen(() -> {});
//then
assertThat(result).isSameAs(ok);
}
@Test
void successVoid_andThen_isCalled() {
//given
ResultVoid ok = Result.ok();
AtomicBoolean called = new AtomicBoolean(false);
//when
ok.andThen(() -> called.set(true));
//then
assertThat(called).isTrue();
}
@Test
void successVoid_andThen_exception_errorVoid() {
//given
ResultVoid ok = Result.ok();
//when
ResultVoid result = ok.andThen(() -> {throw new RuntimeException();});
//then
assertThat(result.isError()).isTrue();
}
@Test
void errorVoid_andThen_returnsSelf() {
//given
ResultVoid error = Result.error(new RuntimeException());
//when
ResultVoid result = error.andThen(() -> {});
//then
assertThat(result).isSameAs(error);
}
@Test
void errorVoid_andThen_notCalled() {
//given
ResultVoid error = Result.error(new RuntimeException());
final AtomicBoolean called = new AtomicBoolean(false);
//when
error.andThen(() -> called.set(true));
//then
assertThat(called).isFalse();
}
}
/** /**
* These include snippets from the Javadocs and are meant to prove that the examples are valid. * These include snippets from the Javadocs and are meant to prove that the examples are valid.
*/ */
@ -2293,7 +2293,7 @@ class ResultTest implements WithAssertions {
Result<Double> businessOperation(final String fileName1, final String fileName2) { Result<Double> businessOperation(final String fileName1, final String fileName2) {
return readIntFromFile(fileName1) return readIntFromFile(fileName1)
.map(this::adjustValue) .andThen(intFromFile1 -> () -> adjustValue(intFromFile1))
.flatMap(adjustedIntFromFile1 -> readIntFromFile(fileName2) .flatMap(adjustedIntFromFile1 -> readIntFromFile(fileName2)
.flatMap(intFromFile2 -> adjustedIntFromFile1 .flatMap(intFromFile2 -> adjustedIntFromFile1
.flatMap(aif1 -> calculateAverage(aif1, intFromFile2)))); .flatMap(aif1 -> calculateAverage(aif1, intFromFile2))));

View file

@ -1,7 +1,10 @@
package net.kemitix.mon; package net.kemitix.mon;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
@ -15,6 +18,8 @@ class WrapperTest {
interface WrappedIterableString extends Wrapper<Iterable<String>> { } interface WrappedIterableString extends Wrapper<Iterable<String>> { }
interface WrappedListInteger extends Wrapper<List<Integer>> {}
interface AWrapper extends Wrapper<String> {} interface AWrapper extends Wrapper<String> {}
@Test @Test
@ -60,7 +65,6 @@ class WrapperTest {
final WrappedString wrappedString = () -> "1"; final WrappedString wrappedString = () -> "1";
final WrappedInteger wrappedInteger = () -> 1; final WrappedInteger wrappedInteger = () -> 1;
//then //then
//noinspection AssertBetweenInconvertibleTypes
assertThat(wrappedString).isNotEqualTo(wrappedInteger); assertThat(wrappedString).isNotEqualTo(wrappedInteger);
} }