diff --git a/src/main/java/net/kemitix/mon/result/Err.java b/src/main/java/net/kemitix/mon/result/Err.java new file mode 100644 index 0000000..e454ade --- /dev/null +++ b/src/main/java/net/kemitix/mon/result/Err.java @@ -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 the type of the value in the Result if it has been a success + */ +@RequiredArgsConstructor +class Err implements Result { + + private final Throwable error; + + @Override + public boolean isError() { + return true; + } + + @Override + public boolean isOkay() { + return false; + } + + @Override + public Result flatMap(final Function> f) { + return Result.error(error); + } + + @Override + public void match(final Consumer onSuccess, final Consumer onError) { + onError.accept(error); + } +} diff --git a/src/main/java/net/kemitix/mon/result/Result.java b/src/main/java/net/kemitix/mon/result/Result.java new file mode 100644 index 0000000..ff36be4 --- /dev/null +++ b/src/main/java/net/kemitix/mon/result/Result.java @@ -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 the type of the result when a success + * + * @author Paul Campbell (pcampbell@kemitix.net) + */ +public interface Result { + + /** + * Create a Result for a success. + * + * @param value the value + * @param the type of the value + * @return a successful Result + */ + static Result ok(final T value) { + return new Success(value); + } + + /** + * Create a Result for an error. + * + * @param error the error (exception) + * @param the type had the result been a success + * @return an error Result + */ + static Result error(final Throwable error) { + return new Err(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 the type of the result of the mapping function + * + * @return a Result + */ + Result flatMap(Function> 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 onSuccess, Consumer onError); + +} diff --git a/src/main/java/net/kemitix/mon/result/Success.java b/src/main/java/net/kemitix/mon/result/Success.java new file mode 100644 index 0000000..413d7c5 --- /dev/null +++ b/src/main/java/net/kemitix/mon/result/Success.java @@ -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 the type of the value in the Result + */ +@RequiredArgsConstructor +class Success implements Result { + + private final T value; + + @Override + public boolean isError() { + return false; + } + + @Override + public boolean isOkay() { + return true; + } + + @Override + public Result flatMap(final Function> f) { + return f.apply(value); + } + + @Override + public void match(final Consumer onSuccess, final Consumer onError) { + onSuccess.accept(value); + } +} diff --git a/src/main/java/net/kemitix/mon/result/package-info.java b/src/main/java/net/kemitix/mon/result/package-info.java new file mode 100644 index 0000000..55f2926 --- /dev/null +++ b/src/main/java/net/kemitix/mon/result/package-info.java @@ -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. + * + *

Ideas initially lifted from the Design with Types series at https://fsharpforfunandprofit.com/

+ * + * @author Paul Campbell (pcampbell@kemitix.net) + */ + +package net.kemitix.mon.result; diff --git a/src/test/java/net/kemitix/mon/ResultTest.java b/src/test/java/net/kemitix/mon/ResultTest.java new file mode 100644 index 0000000..0a66d71 --- /dev/null +++ b/src/test/java/net/kemitix/mon/ResultTest.java @@ -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 result = Result.ok("good"); + //then + assertThat(result.isOkay()).isTrue(); + } + + @Test + public void createSuccess_isNotError() { + //when + final Result result = Result.ok("good"); + //then + assertThat(result.isError()).isFalse(); + } + + @Test + public void createSuccess_matchSuccess() { + //given + final Result 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 result = Result.error(new Exception()); + //then + assertThat(result.isOkay()).isFalse(); + } + + @Test + public void createError_isNotSuccess() { + //when + final Result result = Result.error(new Exception()); + //then + assertThat(result.isError()).isTrue(); + } + + @Test + public void createError_matchError() { + //given + final Result 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 result = Result.ok("good"); + //when + final Result 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 result = Result.ok("good"); + //when + final Result 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 result = Result.error(new Exception("bad")); + //when + final Result flatMap = result.flatMap(v -> Result.ok(v.toUpperCase())); + //then + assertThat(flatMap.isError()).isTrue(); + } + + @Test + public void errorFlatMap_error_isError() { + //given + final Result result = Result.error(new Exception("bad")); + //when + final Result flatMap = result.flatMap(v -> Result.error(new Exception("bad flat map"))); + //then + assertThat(flatMap.isError()).isTrue(); + } +} \ No newline at end of file