Merge branch 'result' into 'master'

Add Result<T>

See merge request kemitix/mon!33
This commit is contained in:
Paul Campbell 2018-06-21 17:23:50 +00:00
commit 7cae02bc58
6 changed files with 343 additions and 1 deletions

View file

@ -4,7 +4,7 @@ CHANGELOG
0.7.0
-----
*
* Add `Result`
0.6.0
-----

View file

@ -0,0 +1,58 @@
/**
* The MIT License (MIT)
*
* Copyright (c) 2017 Paul Campbell
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software
* and associated documentation files (the "Software"), to deal in the Software without restriction,
* including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies
* or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
* AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package net.kemitix.mon.result;
import lombok.RequiredArgsConstructor;
import java.util.function.Consumer;
import java.util.function.Function;
/**
* An Error Result.
*
* @param <T> the type of the value in the Result if it has been a success
*/
@RequiredArgsConstructor
class Err<T> implements Result<T> {
private final Throwable error;
@Override
public boolean isError() {
return true;
}
@Override
public boolean isOkay() {
return false;
}
@Override
public <R> Result<R> flatMap(final Function<T, Result<R>> f) {
return Result.error(error);
}
@Override
public void match(final Consumer<T> onSuccess, final Consumer<Throwable> onError) {
onError.accept(error);
}
}

View file

@ -0,0 +1,90 @@
/**
* The MIT License (MIT)
*
* Copyright (c) 2017 Paul Campbell
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software
* and associated documentation files (the "Software"), to deal in the Software without restriction,
* including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies
* or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
* AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package net.kemitix.mon.result;
import java.util.function.Consumer;
import java.util.function.Function;
/**
* An Either type for holding a result or an error (exception).
*
* @param <T> the type of the result when a success
*
* @author Paul Campbell (pcampbell@kemitix.net)
*/
public interface Result<T> {
/**
* Create a Result for a success.
*
* @param value the value
* @param <T> the type of the value
* @return a successful Result
*/
static <T> Result<T> ok(final T value) {
return new Success<T>(value);
}
/**
* Create a Result for an error.
*
* @param error the error (exception)
* @param <T> the type had the result been a success
* @return an error Result
*/
static <T> Result<T> error(final Throwable error) {
return new Err<T>(error);
}
/**
* Checks of the Result is an error.
*
* @return true if the Result is an error.
*/
boolean isError();
/**
* Checks of the Result is a success.
*
* @return true if the Result is a success.
*/
boolean isOkay();
/**
* Returns a new Result consisting of the result of applying the function to the contents of the Result.
*
* @param f the mapping function the produces a Result
* @param <R> the type of the result of the mapping function
*
* @return a Result
*/
<R> Result<R> flatMap(Function<T, Result<R>> f);
/**
* Matches the Result, either success or error, and supplies the appropriate Consumer with the value or error.
*
* @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
*/
void match(Consumer<T> onSuccess, Consumer<Throwable> onError);
}

View file

@ -0,0 +1,58 @@
/**
* The MIT License (MIT)
*
* Copyright (c) 2017 Paul Campbell
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software
* and associated documentation files (the "Software"), to deal in the Software without restriction,
* including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies
* or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
* AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package net.kemitix.mon.result;
import lombok.RequiredArgsConstructor;
import java.util.function.Consumer;
import java.util.function.Function;
/**
* A Successful Result.
*
* @param <T> the type of the value in the Result
*/
@RequiredArgsConstructor
class Success<T> implements Result<T> {
private final T value;
@Override
public boolean isError() {
return false;
}
@Override
public boolean isOkay() {
return true;
}
@Override
public <R> Result<R> flatMap(final Function<T, Result<R>> f) {
return f.apply(value);
}
@Override
public void match(final Consumer<T> onSuccess, final Consumer<Throwable> onError) {
onSuccess.accept(value);
}
}

View file

@ -0,0 +1,30 @@
/**
* The MIT License (MIT)
*
* Copyright (c) 2017 Paul Campbell
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software
* and associated documentation files (the "Software"), to deal in the Software without restriction,
* including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies
* or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
* AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
/**
* An experiment in creating something similar to a Type-Alias in Java.
*
* <p>Ideas initially lifted from the Design with Types series at https://fsharpforfunandprofit.com/</p>
*
* @author Paul Campbell (pcampbell@kemitix.net)
*/
package net.kemitix.mon.result;

View file

@ -0,0 +1,106 @@
package net.kemitix.mon;
import net.kemitix.mon.result.Result;
import org.assertj.core.api.WithAssertions;
import org.junit.Test;
public class ResultTest implements WithAssertions {
@Test
public void createSuccess_isSuccess() {
//when
final Result<String> result = Result.ok("good");
//then
assertThat(result.isOkay()).isTrue();
}
@Test
public void createSuccess_isNotError() {
//when
final Result<String> result = Result.ok("good");
//then
assertThat(result.isError()).isFalse();
}
@Test
public void createSuccess_matchSuccess() {
//given
final Result<String> result = Result.ok("good");
//then
result.match(
success -> assertThat(success).isEqualTo("good"),
error -> fail("not an error")
);
}
@Test
public void createError_isError() {
//when
final Result<String> result = Result.error(new Exception());
//then
assertThat(result.isOkay()).isFalse();
}
@Test
public void createError_isNotSuccess() {
//when
final Result<String> result = Result.error(new Exception());
//then
assertThat(result.isError()).isTrue();
}
@Test
public void createError_matchError() {
//given
final Result<Object> result = Result.error(new Exception("bad"));
//then
result.match(
success -> fail("not a success"),
error -> assertThat(error.getMessage()).isEqualTo("bad")
);
}
@Test
public void successFlatMap_success_isSuccess() {
//given
final Result<String> result = Result.ok("good");
//when
final Result<String> flatMap = result.flatMap(v -> Result.ok(v.toUpperCase()));
//then
assertThat(flatMap.isOkay()).isTrue();
flatMap.match(
success -> assertThat(success).isEqualTo("GOOD"),
error -> fail("not an error")
);
}
@Test
public void successFlatMap_error_isError() {
//given
final Result<String> result = Result.ok("good");
//when
final Result<String> flatMap = result.flatMap(v -> Result.error(new Exception("bad flat map")));
//then
assertThat(flatMap.isOkay()).isFalse();
}
@Test
public void errorFlatMap_success_isError() {
//given
final Result<String> result = Result.error(new Exception("bad"));
//when
final Result<String> flatMap = result.flatMap(v -> Result.ok(v.toUpperCase()));
//then
assertThat(flatMap.isError()).isTrue();
}
@Test
public void errorFlatMap_error_isError() {
//given
final Result<String> result = Result.error(new Exception("bad"));
//when
final Result<String> flatMap = result.flatMap(v -> Result.error(new Exception("bad flat map")));
//then
assertThat(flatMap.isError()).isTrue();
}
}