├── .gitignore ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── src ├── main │ └── java │ │ └── personthecat │ │ └── fresult │ │ ├── Void.java │ │ ├── functions │ │ ├── ThrowingRunnable.java │ │ ├── ThrowingSupplier.java │ │ ├── ThrowingConsumer.java │ │ ├── ThrowingFunction.java │ │ ├── ThrowingBiConsumer.java │ │ ├── ThrowingBiFunction.java │ │ ├── ThrowingOptionalSupplier.java │ │ ├── ThrowingOptionalFunction.java │ │ ├── ResultFunction.java │ │ ├── ThrowingOptionalBiFunction.java │ │ └── OptionalResultFunction.java │ │ ├── exception │ │ ├── MissingProcedureException.java │ │ ├── ResultUnwrapException.java │ │ └── WrongErrorException.java │ │ ├── BasicResult.java │ │ ├── Shorthand.java │ │ ├── Resolver.java │ │ ├── Protocol.java │ │ ├── WithResource.java │ │ ├── PartialResult.java │ │ ├── PartialOptionalResult.java │ │ ├── WithResources.java │ │ └── OptionalResult.java └── test │ └── java │ └── personthecat │ └── fresult │ ├── util │ ├── TestFlag.java │ ├── TestCloseableResource.java │ └── TestUtils.java │ ├── ShorthandTest.java │ ├── PartialResultTest.java │ ├── PartialOptionalResultTest.java │ ├── WithResourceTest.java │ ├── ResolverTest.java │ ├── ProtocolTest.java │ ├── WithResourcesTest.java │ ├── OptionalResultTest.java │ └── ResultTest.java ├── .github └── workflows │ └── gradle.yml ├── gradle.properties ├── gradlew.bat ├── gradlew ├── README.md └── LICENSE /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .gradle/ 3 | build/ 4 | out/ 5 | .idea 6 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PersonTheCat/FResult/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /src/main/java/personthecat/fresult/Void.java: -------------------------------------------------------------------------------- 1 | package personthecat.fresult; 2 | 3 | /** The return type of results with no practical return type. */ 4 | public enum Void { INSTANCE } -------------------------------------------------------------------------------- /src/main/java/personthecat/fresult/functions/ThrowingRunnable.java: -------------------------------------------------------------------------------- 1 | package personthecat.fresult.functions; 2 | 3 | @FunctionalInterface 4 | public interface ThrowingRunnable { 5 | void run() throws E; 6 | } -------------------------------------------------------------------------------- /src/main/java/personthecat/fresult/functions/ThrowingSupplier.java: -------------------------------------------------------------------------------- 1 | package personthecat.fresult.functions; 2 | 3 | @FunctionalInterface 4 | public interface ThrowingSupplier { 5 | T get() throws E; 6 | } -------------------------------------------------------------------------------- /src/main/java/personthecat/fresult/functions/ThrowingConsumer.java: -------------------------------------------------------------------------------- 1 | package personthecat.fresult.functions; 2 | 3 | @FunctionalInterface 4 | public interface ThrowingConsumer { 5 | void accept(T t) throws E; 6 | } -------------------------------------------------------------------------------- /src/main/java/personthecat/fresult/functions/ThrowingFunction.java: -------------------------------------------------------------------------------- 1 | package personthecat.fresult.functions; 2 | 3 | @FunctionalInterface 4 | public interface ThrowingFunction { 5 | R apply(T t) throws E; 6 | } -------------------------------------------------------------------------------- /src/main/java/personthecat/fresult/functions/ThrowingBiConsumer.java: -------------------------------------------------------------------------------- 1 | package personthecat.fresult.functions; 2 | 3 | @FunctionalInterface 4 | public interface ThrowingBiConsumer { 5 | void accept(T1 t1, T2 t2) throws E; 6 | } -------------------------------------------------------------------------------- /src/main/java/personthecat/fresult/functions/ThrowingBiFunction.java: -------------------------------------------------------------------------------- 1 | package personthecat.fresult.functions; 2 | 3 | @FunctionalInterface 4 | public interface ThrowingBiFunction { 5 | R apply(T1 t1, T2 t2) throws E; 6 | } -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-bin.zip 4 | networkTimeout=10000 5 | zipStoreBase=GRADLE_USER_HOME 6 | zipStorePath=wrapper/dists 7 | -------------------------------------------------------------------------------- /src/main/java/personthecat/fresult/functions/ThrowingOptionalSupplier.java: -------------------------------------------------------------------------------- 1 | package personthecat.fresult.functions; 2 | 3 | import java.util.Optional; 4 | 5 | @FunctionalInterface 6 | public interface ThrowingOptionalSupplier { 7 | Optional get() throws E; 8 | } 9 | -------------------------------------------------------------------------------- /src/main/java/personthecat/fresult/functions/ThrowingOptionalFunction.java: -------------------------------------------------------------------------------- 1 | package personthecat.fresult.functions; 2 | 3 | import java.util.Optional; 4 | 5 | @FunctionalInterface 6 | public interface ThrowingOptionalFunction { 7 | Optional apply(T val) throws E; 8 | } 9 | -------------------------------------------------------------------------------- /src/main/java/personthecat/fresult/functions/ResultFunction.java: -------------------------------------------------------------------------------- 1 | package personthecat.fresult.functions; 2 | 3 | import personthecat.fresult.Result; 4 | 5 | import java.util.function.Function; 6 | 7 | @FunctionalInterface 8 | public interface ResultFunction extends Function> {} 9 | -------------------------------------------------------------------------------- /src/main/java/personthecat/fresult/functions/ThrowingOptionalBiFunction.java: -------------------------------------------------------------------------------- 1 | package personthecat.fresult.functions; 2 | 3 | import java.util.Optional; 4 | 5 | @FunctionalInterface 6 | public interface ThrowingOptionalBiFunction { 7 | Optional apply(T1 t1, T2 t2) throws E; 8 | } 9 | -------------------------------------------------------------------------------- /src/test/java/personthecat/fresult/util/TestFlag.java: -------------------------------------------------------------------------------- 1 | package personthecat.fresult.util; 2 | 3 | public class TestFlag { 4 | boolean flag = false; 5 | 6 | public void set(final boolean flag) { 7 | this.flag = flag; 8 | } 9 | 10 | public boolean isFlagged() { 11 | return this.flag; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/personthecat/fresult/exception/MissingProcedureException.java: -------------------------------------------------------------------------------- 1 | package personthecat.fresult.exception; 2 | 3 | /** Called whenever no procedure is defined for handling an error. */ 4 | public class MissingProcedureException extends RuntimeException { 5 | public MissingProcedureException(final Throwable e) { 6 | super(e); 7 | } 8 | } -------------------------------------------------------------------------------- /src/main/java/personthecat/fresult/functions/OptionalResultFunction.java: -------------------------------------------------------------------------------- 1 | package personthecat.fresult.functions; 2 | 3 | import personthecat.fresult.OptionalResult; 4 | 5 | import java.util.function.Function; 6 | 7 | @FunctionalInterface 8 | public interface OptionalResultFunction extends Function> {} 9 | -------------------------------------------------------------------------------- /src/main/java/personthecat/fresult/exception/ResultUnwrapException.java: -------------------------------------------------------------------------------- 1 | package personthecat.fresult.exception; 2 | 3 | /** 4 | * Thrown whenever an unwrap is attempted on a Result despite no value 5 | * being present. 6 | */ 7 | public class ResultUnwrapException extends RuntimeException { 8 | public ResultUnwrapException(final String msg) { 9 | super(msg); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/test/java/personthecat/fresult/util/TestCloseableResource.java: -------------------------------------------------------------------------------- 1 | package personthecat.fresult.util; 2 | 3 | public class TestCloseableResource implements AutoCloseable { 4 | boolean closed = false; 5 | 6 | @Override 7 | public void close() { 8 | this.closed = true; 9 | } 10 | 11 | public boolean isClosed() { 12 | return this.closed; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/personthecat/fresult/exception/WrongErrorException.java: -------------------------------------------------------------------------------- 1 | package personthecat.fresult.exception; 2 | 3 | /** 4 | * An error thrown whenever an unexpected type of exception is 5 | * caught by a Result wrapper. 6 | */ 7 | public class WrongErrorException extends RuntimeException { 8 | public WrongErrorException(final String msg, final Throwable e) { 9 | super(msg, e); 10 | } 11 | } -------------------------------------------------------------------------------- /src/test/java/personthecat/fresult/ShorthandTest.java: -------------------------------------------------------------------------------- 1 | package personthecat.fresult; 2 | 3 | import org.junit.jupiter.api.Test; 4 | 5 | import static org.junit.jupiter.api.Assertions.assertEquals; 6 | 7 | public final class ShorthandTest { 8 | 9 | @Test 10 | public void format_interpolatesValues() { 11 | assertEquals("1.2.hello", Shorthand.f("{}.{}.{}", 1, 2, "hello")); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /.github/workflows/gradle.yml: -------------------------------------------------------------------------------- 1 | # This workflow will build a Java project with Gradle 2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-gradle 3 | 4 | name: Java CI with Gradle 5 | 6 | on: 7 | push: 8 | branches: [ master ] 9 | pull_request: 10 | branches: [ master ] 11 | 12 | jobs: 13 | build: 14 | 15 | runs-on: ubuntu-latest 16 | 17 | steps: 18 | - uses: actions/checkout@v2 19 | - name: Set up JDK 8 20 | uses: actions/setup-java@v2 21 | with: 22 | java-version: '8' 23 | distribution: 'adopt' 24 | - name: Grant execute permission for gradlew 25 | run: chmod +x gradlew 26 | - name: Build with Gradle 27 | run: ./gradlew build 28 | -------------------------------------------------------------------------------- /src/test/java/personthecat/fresult/util/TestUtils.java: -------------------------------------------------------------------------------- 1 | package personthecat.fresult.util; 2 | 3 | import java.io.IOException; 4 | 5 | @SuppressWarnings("UnusedReturnValue") 6 | public final class TestUtils { 7 | 8 | public static final String DUMMY_OUTPUT = "dummy"; 9 | 10 | private TestUtils() {} 11 | 12 | public static String throwFalseRE() throws IOException { 13 | throw new RuntimeException("Actual error type not caught by handler."); 14 | } 15 | 16 | public static String throwsIOE() throws IOException { 17 | throw new IOException("Error not caught by handler."); 18 | } 19 | 20 | public static String returnsDummy() { 21 | return DUMMY_OUTPUT; 22 | } 23 | 24 | public static String returnsNull() { 25 | return null; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | 2 | version=3.1 3 | group=com.personthecat 4 | description=A unified type for optional and error-prone procedures in Java. 5 | 6 | pom_name=FResult 7 | pom_description=A unified type for optional and error-prone procedures in Java. 8 | pom_url=https://github.com/PersonTheCat/FResult 9 | pom_license_name=LGPLv2 10 | pom_license_url=https://github.com/PersonTheCat/FResult/blob/main/LICENSE 11 | 12 | pom_artifact_id=fresult 13 | pom_developer=PersonTheCat 14 | pom_developer_name=Dalton Richardson 15 | pom_developer_email=professionallyperson@gmail.com 16 | pom_packaging=jar 17 | 18 | pom_scm_connection='scm:git:git://github.com/PersonTheCat/FResult.git' 19 | pom_scm_developer_connection='scm:git:ssh://github.com:PersonTheCat/Fresult.git' 20 | pom_scm_url='http://github.com/PersonTheCat/Fresult/tree/main' -------------------------------------------------------------------------------- /src/test/java/personthecat/fresult/PartialResultTest.java: -------------------------------------------------------------------------------- 1 | package personthecat.fresult; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import personthecat.fresult.util.TestFlag; 5 | 6 | import static org.junit.jupiter.api.Assertions.assertFalse; 7 | import static org.junit.jupiter.api.Assertions.assertTrue; 8 | 9 | public final class PartialResultTest { 10 | 11 | @Test 12 | @SuppressWarnings("unused") 13 | public void partialResult_doesNotExecute() { 14 | final TestFlag flag = new TestFlag(); 15 | final PartialResult r = Result.of(() -> flag.set(true)); 16 | assertFalse(flag.isFlagged()); 17 | } 18 | 19 | @Test 20 | public void ifErr_doesExecute() { 21 | final TestFlag flag = new TestFlag(); 22 | Result.of(() -> flag.set(true)).ifErr(Result::IGNORE); 23 | assertTrue(flag.isFlagged()); 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /src/test/java/personthecat/fresult/PartialOptionalResultTest.java: -------------------------------------------------------------------------------- 1 | package personthecat.fresult; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import personthecat.fresult.util.TestFlag; 5 | 6 | import static org.junit.jupiter.api.Assertions.assertFalse; 7 | import static org.junit.jupiter.api.Assertions.assertTrue; 8 | 9 | public final class PartialOptionalResultTest { 10 | 11 | @Test 12 | @SuppressWarnings("unused") 13 | public void partialResult_doesNotExecute() { 14 | final TestFlag flag = new TestFlag(); 15 | final PartialOptionalResult r = Result.nullable(() -> { flag.set(true); return ""; }); 16 | assertFalse(flag.isFlagged()); 17 | } 18 | 19 | @Test 20 | public void ifErr_doesExecute() { 21 | final TestFlag flag = new TestFlag(); 22 | Result.nullable(() -> { flag.set(true); return ""; }).ifErr(Result::IGNORE); 23 | assertTrue(flag.isFlagged()); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/test/java/personthecat/fresult/WithResourceTest.java: -------------------------------------------------------------------------------- 1 | package personthecat.fresult; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import personthecat.fresult.util.TestCloseableResource; 5 | import personthecat.fresult.util.TestFlag; 6 | 7 | import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; 8 | import static org.junit.jupiter.api.Assertions.assertFalse; 9 | import static org.junit.jupiter.api.Assertions.assertTrue; 10 | 11 | public final class WithResourceTest { 12 | 13 | @Test 14 | public void withResource_closesResource() { 15 | final TestCloseableResource closeable = new TestCloseableResource(); 16 | Result.with(() -> closeable).suppress(c -> {}); 17 | 18 | assertTrue(closeable.isClosed()); 19 | } 20 | 21 | @Test 22 | public void withResource_supplierHandlesException() { 23 | final TestFlag flag = new TestFlag(); 24 | 25 | assertDoesNotThrow(() -> 26 | Result.with(() -> { throw new NullPointerException(); }) 27 | .suppress(t -> { flag.set(true); return ""; })); 28 | 29 | assertFalse(flag.isFlagged()); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/test/java/personthecat/fresult/ResolverTest.java: -------------------------------------------------------------------------------- 1 | package personthecat.fresult; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import personthecat.fresult.exception.MissingProcedureException; 5 | import personthecat.fresult.util.TestUtils; 6 | 7 | import static org.junit.jupiter.api.Assertions.assertEquals; 8 | import static org.junit.jupiter.api.Assertions.assertThrows; 9 | 10 | public final class ResolverTest { 11 | 12 | @Test 13 | public void resolver_handlesSpecificError() { 14 | final Resolver r = Result.resolve(IllegalArgumentException.class, e -> "") 15 | .resolve(NullPointerException.class, e -> TestUtils.DUMMY_OUTPUT); 16 | 17 | assertEquals(TestUtils.DUMMY_OUTPUT, r.expose(() -> { throw new NullPointerException(); })); 18 | } 19 | 20 | @Test 21 | public void emptyResolver_throwsMissingProcedure() { 22 | final Resolver r = new Resolver<>(); 23 | final MissingProcedureException e = assertThrows(MissingProcedureException.class, 24 | () -> r.expose(() -> { throw new NullPointerException(); })); 25 | 26 | assertEquals(NullPointerException.class, e.getCause().getClass()); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/test/java/personthecat/fresult/ProtocolTest.java: -------------------------------------------------------------------------------- 1 | package personthecat.fresult; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import personthecat.fresult.exception.MissingProcedureException; 5 | import personthecat.fresult.util.TestFlag; 6 | 7 | import static org.junit.jupiter.api.Assertions.assertEquals; 8 | import static org.junit.jupiter.api.Assertions.assertThrows; 9 | import static org.junit.jupiter.api.Assertions.assertTrue; 10 | 11 | public final class ProtocolTest { 12 | 13 | @Test 14 | public void protocol_handlesSpecificError() { 15 | final TestFlag flag = new TestFlag(); 16 | final Protocol p = Result.define(IllegalArgumentException.class, e -> flag.set(false)) 17 | .define(NullPointerException.class, e -> flag.set(true)); 18 | 19 | p.run(() -> { throw new NullPointerException(); }); 20 | assertTrue(flag.isFlagged()); 21 | } 22 | 23 | @Test 24 | public void emptyProtocol_throwsMissingProcedure() { 25 | final Protocol p = new Protocol(); 26 | final MissingProcedureException e = assertThrows(MissingProcedureException.class, 27 | () -> p.run(() -> { throw new NullPointerException(); })); 28 | 29 | assertEquals(NullPointerException.class, e.getCause().getClass()); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/test/java/personthecat/fresult/WithResourcesTest.java: -------------------------------------------------------------------------------- 1 | package personthecat.fresult; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import personthecat.fresult.util.TestCloseableResource; 5 | import personthecat.fresult.util.TestFlag; 6 | 7 | import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; 8 | import static org.junit.jupiter.api.Assertions.assertFalse; 9 | import static org.junit.jupiter.api.Assertions.assertTrue; 10 | 11 | public final class WithResourcesTest { 12 | 13 | @Test 14 | public void withResources_closesResources() { 15 | final TestCloseableResource closeable1 = new TestCloseableResource(); 16 | final TestCloseableResource closeable2 = new TestCloseableResource(); 17 | Result.with(() -> closeable1).with(c1 -> closeable2).suppress((c1, c2) -> {}); 18 | 19 | assertTrue(closeable1.isClosed()); 20 | assertTrue(closeable2.isClosed()); 21 | } 22 | 23 | @Test 24 | public void withResources_supplierHandlesException() { 25 | final TestFlag flag = new TestFlag(); 26 | 27 | final WithResource r1 = 28 | Result.with(() -> { throw new NullPointerException(); }); 29 | 30 | assertDoesNotThrow(() -> 31 | r1.with(() -> { throw new NullPointerException(); }) 32 | .suppress(t -> { flag.set(true); return ""; })); 33 | 34 | assertFalse(flag.isFlagged()); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/personthecat/fresult/BasicResult.java: -------------------------------------------------------------------------------- 1 | package personthecat.fresult; 2 | 3 | import org.jetbrains.annotations.CheckReturnValue; 4 | import personthecat.fresult.exception.ResultUnwrapException; 5 | 6 | import java.util.Optional; 7 | import java.util.function.Consumer; 8 | 9 | import static personthecat.fresult.Shorthand.f; 10 | 11 | /** 12 | * A common parent interface for all result types. The final hierarchy is WIP. 13 | */ 14 | @SuppressWarnings("unused") 15 | public interface BasicResult { 16 | 17 | /** 18 | * Attempts to retrieve the underlying value, if present, while also accounting for 19 | * any potential errors. 20 | * 21 | * e.g. 22 | *
23 |      *   Result.of(() -> getValueOrFail())
24 |      *     .get(e -> {...})
25 |      *     .ifPresent(t -> {...});
26 |      * 
27 | * 28 | * @param f A handler for any errors that arise. 29 | * @return The underlying value, wrapped in {@link Optional}. 30 | */ 31 | @CheckReturnValue 32 | Optional get(final Consumer f); 33 | 34 | /** 35 | * Attempts to retrieve the underlying value, asserting that one must exist. 36 | * 37 | * @throws ResultUnwrapException Wraps the underlying error, if present. 38 | * @return The underlying value. 39 | */ 40 | default T unwrap() { 41 | return expect("Attempted to unwrap a result with no value."); 42 | } 43 | 44 | /** 45 | * Yields the underlying value, throwing a convenient, generic exception, if an 46 | * error occurs. 47 | * 48 | * e.g. 49 | *
50 |      *   // Runs an unsafe process, wrapping any original errors.
51 |      *   Object result = getResult()
52 |      *     .expect("Unable to get value from result.");
53 |      * 
54 | * 55 | * @throws ResultUnwrapException Wraps the underlying error, if present. 56 | * @param message The message to display in the event of an error. 57 | * @return The underlying value. 58 | */ 59 | T expect(final String message); 60 | 61 | /** 62 | * Formatted variant of {@link #expect}. 63 | * 64 | * @throws ResultUnwrapException Wraps the underlying error, if present. 65 | * @param message The message to display in the event of an error. 66 | * @param args A series of interpolated arguments (replacing {}). 67 | * @return The underlying value 68 | */ 69 | default T expect(final String message, final Object... args) { 70 | return this.expect(f(message, args)); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/test/java/personthecat/fresult/OptionalResultTest.java: -------------------------------------------------------------------------------- 1 | package personthecat.fresult; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import personthecat.fresult.exception.ResultUnwrapException; 5 | import personthecat.fresult.util.TestFlag; 6 | import personthecat.fresult.util.TestUtils; 7 | 8 | import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; 9 | import static org.junit.jupiter.api.Assertions.assertEquals; 10 | import static org.junit.jupiter.api.Assertions.assertThrows; 11 | import static org.junit.jupiter.api.Assertions.assertTrue; 12 | 13 | public final class OptionalResultTest { 14 | 15 | @Test 16 | public void wrapper_catchesError() { 17 | assertDoesNotThrow(() -> Result.nullable(TestUtils::throwsIOE).ifErr(Result::IGNORE)); 18 | } 19 | 20 | @Test 21 | public void wrongError_isRethrown() { 22 | assertThrows(RuntimeException.class, () -> Result.nullable(TestUtils::throwFalseRE).ifErr(Result::IGNORE)); 23 | } 24 | 25 | @Test 26 | public void nullValue_isDetected() { 27 | assertTrue(Result.nullable(TestUtils::returnsNull).ifErr(Result::IGNORE).isEmpty()); 28 | } 29 | 30 | @Test 31 | public void okValue_isDetected() { 32 | assertTrue(Result.nullable(TestUtils::returnsDummy).ifErr(Result::IGNORE).isOk()); 33 | } 34 | 35 | @Test 36 | public void error_isDetected() { 37 | assertTrue(Result.nullable(TestUtils::throwsIOE).ifErr(Result::IGNORE).isErr()); 38 | } 39 | 40 | @Test 41 | public void presentValue_isNotReplaced() { 42 | assertEquals(TestUtils.DUMMY_OUTPUT, Result.nullable(TestUtils.DUMMY_OUTPUT).orElse("")); 43 | } 44 | 45 | @Test 46 | public void absentValue_isReplaced() { 47 | assertEquals(TestUtils.DUMMY_OUTPUT, Result.nullable((String) null).orElse(TestUtils.DUMMY_OUTPUT)); 48 | } 49 | 50 | @Test 51 | public void expectEmpty_onValue_throwsException() { 52 | final OptionalResult r = Result.suppressNullable(TestUtils::returnsDummy); 53 | assertThrows(ResultUnwrapException.class, () -> r.expectEmpty("")); 54 | } 55 | 56 | @Test 57 | public void expectEmpty_onError_throwsException() { 58 | final OptionalResult r = Result.suppressNullable(TestUtils::throwsIOE); 59 | assertThrows(ResultUnwrapException.class, () -> r.expectEmpty("")); 60 | } 61 | 62 | @Test 63 | public void filter_removesValue() { 64 | assertTrue(Result.ok("").filter(t -> false).isEmpty()); 65 | } 66 | 67 | @Test 68 | public void filter_removesError() { 69 | assertTrue(Result.err(new NullPointerException()).filterErr(e -> false).isEmpty()); 70 | } 71 | 72 | @Test 73 | public void ifEmpty_eventRuns() { 74 | final TestFlag flag = new TestFlag(); 75 | Result.nullable((String) null).ifEmpty(() -> flag.set(true)); 76 | 77 | assertTrue(flag.isFlagged()); 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%"=="" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%"=="" set DIRNAME=. 29 | @rem This is normally unused 30 | set APP_BASE_NAME=%~n0 31 | set APP_HOME=%DIRNAME% 32 | 33 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 34 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 35 | 36 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 37 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 38 | 39 | @rem Find java.exe 40 | if defined JAVA_HOME goto findJavaFromJavaHome 41 | 42 | set JAVA_EXE=java.exe 43 | %JAVA_EXE% -version >NUL 2>&1 44 | if %ERRORLEVEL% equ 0 goto execute 45 | 46 | echo. 47 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 48 | echo. 49 | echo Please set the JAVA_HOME variable in your environment to match the 50 | echo location of your Java installation. 51 | 52 | goto fail 53 | 54 | :findJavaFromJavaHome 55 | set JAVA_HOME=%JAVA_HOME:"=% 56 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 57 | 58 | if exist "%JAVA_EXE%" goto execute 59 | 60 | echo. 61 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 62 | echo. 63 | echo Please set the JAVA_HOME variable in your environment to match the 64 | echo location of your Java installation. 65 | 66 | goto fail 67 | 68 | :execute 69 | @rem Setup the command line 70 | 71 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 72 | 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if %ERRORLEVEL% equ 0 goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | set EXIT_CODE=%ERRORLEVEL% 85 | if %EXIT_CODE% equ 0 set EXIT_CODE=1 86 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% 87 | exit /b %EXIT_CODE% 88 | 89 | :mainEnd 90 | if "%OS%"=="Windows_NT" endlocal 91 | 92 | :omega 93 | -------------------------------------------------------------------------------- /src/main/java/personthecat/fresult/Shorthand.java: -------------------------------------------------------------------------------- 1 | package personthecat.fresult; 2 | 3 | import personthecat.fresult.exception.MissingProcedureException; 4 | import personthecat.fresult.exception.ResultUnwrapException; 5 | import personthecat.fresult.exception.WrongErrorException; 6 | 7 | /** 8 | * A collection of shorthand methods intended to improve the readability 9 | * of {@link Result}.java.; 10 | */ 11 | class Shorthand { 12 | 13 | /** 14 | * Prints to the standard error print stream. Would ideally be 15 | * replaced with Log4j or some other logger. 16 | */ 17 | static void warn(final String x, final Object... args) { 18 | System.err.println(f(x, args)); 19 | } 20 | 21 | /** Shorthand for {@link RuntimeException()}. */ 22 | static RuntimeException runEx(final Throwable e) { 23 | return new RuntimeException(e); 24 | } 25 | 26 | static RuntimeException runEx(final String s, final Throwable e) { 27 | return new RuntimeException(s, e); 28 | } 29 | 30 | static RuntimeException runEx(final String s) { 31 | return new RuntimeException(s); 32 | } 33 | 34 | /** Shorthand for {@link ResultUnwrapException()}. */ 35 | static ResultUnwrapException unwrapEx(final String msg) { 36 | return new ResultUnwrapException(msg); 37 | } 38 | 39 | /** Shorthand for {@link WrongErrorException()}. */ 40 | static WrongErrorException wrongErrorEx(final Throwable e) { 41 | return new WrongErrorException("Wrong type of error caught by wrapper.", e); 42 | } 43 | 44 | /** Shorthand for {@link MissingProcedureException()}. */ 45 | static MissingProcedureException missingProcedureEx(final Throwable e) { 46 | return new MissingProcedureException(e); 47 | } 48 | 49 | /** A neater way to interpolate strings. */ 50 | static String f(String s, Object... args) { 51 | int begin = 0, si = 0, oi = 0; 52 | StringBuilder sb = new StringBuilder(); 53 | while (true) { 54 | si = s.indexOf("{}", si); 55 | if (si >= 0) { 56 | sb.append(s, begin, si); 57 | sb.append(args[oi++]); 58 | begin = si = si + 2; 59 | } else { 60 | break; 61 | } 62 | } 63 | sb.append(s.substring(begin)); 64 | return sb.toString(); 65 | } 66 | 67 | /** 68 | * Determines whether this wrapper contains the expected type of error. 69 | * 70 | * @throws WrongErrorException if the result contains an unexpected error. 71 | */ 72 | @SuppressWarnings("rawtypes") 73 | static boolean checkError(final BasicResult result, final Class clazz) { 74 | if (result instanceof Result.Error) { 75 | final Throwable error = ((Result.Error) result).expose(); 76 | if (!clazz.isInstance(error)) { 77 | throw wrongErrorEx(error); 78 | } 79 | return true; 80 | } 81 | return false; 82 | } 83 | 84 | /** Attempts to cast the error into the appropriate subclass. */ 85 | @SuppressWarnings("unchecked") 86 | static E errorFound(final Throwable err) { 87 | try { 88 | return (E) err; 89 | } catch (final ClassCastException e) { 90 | throw wrongErrorEx(err); 91 | } 92 | } 93 | } -------------------------------------------------------------------------------- /src/test/java/personthecat/fresult/ResultTest.java: -------------------------------------------------------------------------------- 1 | package personthecat.fresult; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import personthecat.fresult.exception.ResultUnwrapException; 5 | import personthecat.fresult.util.TestFlag; 6 | import personthecat.fresult.util.TestUtils; 7 | 8 | import java.io.IOException; 9 | import java.util.List; 10 | 11 | import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; 12 | import static org.junit.jupiter.api.Assertions.assertEquals; 13 | import static org.junit.jupiter.api.Assertions.assertTrue; 14 | import static org.junit.jupiter.api.Assertions.assertThrows; 15 | 16 | public final class ResultTest { 17 | 18 | @Test 19 | public void wrapper_catchesError() { 20 | assertDoesNotThrow(() -> Result.of(TestUtils::throwsIOE).ifErr(Result::IGNORE)); 21 | } 22 | 23 | @Test 24 | public void wrongError_isRethrown() { 25 | assertThrows(RuntimeException.class, () -> Result.of(TestUtils::throwFalseRE).ifErr(Result::IGNORE)); 26 | } 27 | 28 | @Test 29 | public void nullValue_throwsNPE() { 30 | assertThrows(NullPointerException.class, () -> Result.of(TestUtils::returnsNull).ifErr(Result::IGNORE)); 31 | } 32 | 33 | @Test 34 | public void okValue_isDetected() { 35 | assertTrue(Result.of(TestUtils::returnsDummy).ifErr(Result::IGNORE).isOk()); 36 | } 37 | 38 | @Test 39 | public void error_isDetected() { 40 | assertTrue(Result.of(TestUtils::throwsIOE).ifErr(Result::IGNORE).isErr()); 41 | } 42 | 43 | @Test 44 | public void ifOk_eventRuns() { 45 | final TestFlag flag = new TestFlag(); 46 | Result.of(() -> "").ifErr(Result::IGNORE).ifOk(t -> flag.set(true)); 47 | 48 | assertTrue(flag.isFlagged()); 49 | } 50 | 51 | @Test 52 | public void ifErr_eventRuns() { 53 | final TestFlag flag = new TestFlag(); 54 | Result.of(TestUtils::throwsIOE).ifErr(e -> flag.set(true)); 55 | 56 | assertTrue(flag.isFlagged()); 57 | } 58 | 59 | @Test 60 | public void andThen_eventRuns() { 61 | final TestFlag flag = new TestFlag(); 62 | Result.of(() -> "").ifErr(Result::IGNORE).andThen(() -> flag.set(true)); 63 | 64 | assertTrue(flag.isFlagged()); 65 | } 66 | 67 | @Test 68 | public void orElseTry_returnsNewValue() { 69 | final Result r = Result.suppress(TestUtils::throwsIOE) 70 | .orElseTry(TestUtils::returnsDummy) 71 | .ifErr(Result::IGNORE); 72 | 73 | assertEquals(TestUtils.DUMMY_OUTPUT, r.resolve(e -> "").expose()); 74 | } 75 | 76 | @Test 77 | public void foldValue_returnsNewValue() { 78 | final String s = Result.of(() -> "") 79 | .ifErr(Result::IGNORE) 80 | .fold(t -> TestUtils.DUMMY_OUTPUT, e -> ""); 81 | 82 | assertEquals(TestUtils.DUMMY_OUTPUT, s); 83 | } 84 | 85 | @Test 86 | public void foldError_returnsNewValue() { 87 | final String s = Result.of(TestUtils::throwsIOE) 88 | .ifErr(Result::IGNORE) 89 | .fold(t -> "", e -> TestUtils.DUMMY_OUTPUT); 90 | 91 | assertEquals(TestUtils.DUMMY_OUTPUT, s); 92 | } 93 | 94 | @Test 95 | public void mapValue_returnsMappedValue() { 96 | final int hash = TestUtils.DUMMY_OUTPUT.hashCode(); 97 | assertEquals(hash, (int) Result.ok(TestUtils.DUMMY_OUTPUT).map(String::hashCode).expose()); 98 | } 99 | 100 | @Test 101 | public void mapError_returnsMappedError() { 102 | final Exception mapped = new Exception(TestUtils.DUMMY_OUTPUT); 103 | assertEquals(mapped, Result.err(new Exception()).mapErr(e -> mapped).expose()); 104 | } 105 | 106 | @Test 107 | public void expect_onError_throwsException() { 108 | final Result r = Result.suppress(TestUtils::throwsIOE); 109 | assertThrows(ResultUnwrapException.class, () -> r.expect("")); 110 | } 111 | 112 | @Test 113 | public void expectErr_onValue_throwsException() { 114 | final Result r = Result.suppress(TestUtils::returnsDummy); 115 | assertThrows(ResultUnwrapException.class, () -> r.expectErr("").wait()); 116 | } 117 | 118 | @Test 119 | public void filter_yieldsError() { 120 | assertTrue(Result.ok("").filter(t -> false, RuntimeException::new).isErr()); 121 | } 122 | 123 | @Test 124 | public void filter_yieldsValue() { 125 | assertTrue(Result.err(new NullPointerException()).filterErr(e -> false, Object::new).isOk()); 126 | } 127 | 128 | @Test 129 | public void throwIfErr_onError_throwsException() { 130 | final Result r = Result.suppress(TestUtils::throwsIOE); 131 | assertThrows(IOException.class, r::throwIfErr); 132 | } 133 | 134 | @Test 135 | public void throwIfErr_onValue_doesNotThrowException() { 136 | final Result r = Result.suppress(TestUtils::returnsDummy); 137 | assertDoesNotThrow(r::throwIfErr); 138 | } 139 | 140 | @Test 141 | public void join_yieldsError() { 142 | final Result joint = Result.join( 143 | Result.of(() -> {}), 144 | Result.of(() -> { throw new RuntimeException(TestUtils.DUMMY_OUTPUT); }), 145 | Result.of(() -> {}) 146 | ).ifErr(Result::IGNORE); 147 | 148 | assertTrue(joint.isErr()); 149 | assertEquals(TestUtils.DUMMY_OUTPUT, ((Result.Error) joint).expose().getMessage()); 150 | } 151 | 152 | @Test 153 | public void collect_yieldsCollection() { 154 | final Result, Throwable> list = Result.collect( 155 | Result.ok(""), 156 | Result.ok(TestUtils.DUMMY_OUTPUT), 157 | Result.ok("") 158 | ).ifErr(Result::IGNORE); 159 | 160 | assertTrue(list.isOk()); 161 | assertTrue(((Result.Value, Throwable>) list).expose().contains(TestUtils.DUMMY_OUTPUT)); 162 | } 163 | 164 | } 165 | -------------------------------------------------------------------------------- /src/main/java/personthecat/fresult/Resolver.java: -------------------------------------------------------------------------------- 1 | package personthecat.fresult; 2 | 3 | import org.jetbrains.annotations.CheckReturnValue; 4 | import personthecat.fresult.exception.MissingProcedureException; 5 | import personthecat.fresult.functions.ThrowingOptionalSupplier; 6 | import personthecat.fresult.functions.ThrowingSupplier; 7 | 8 | import java.util.HashSet; 9 | import java.util.Objects; 10 | import java.util.Optional; 11 | import java.util.Set; 12 | import java.util.function.Function; 13 | 14 | import static personthecat.fresult.Shorthand.missingProcedureEx; 15 | 16 | /** 17 | * A set of procedures for handling specific error types. 18 | * 19 | * @param The type of value being returned by this wrapper. 20 | */ 21 | @SuppressWarnings({"unused", "UnusedReturnValue"}) 22 | public class Resolver { 23 | 24 | private final Set> procedures = new HashSet<>(); 25 | 26 | /** 27 | * Indicates that this type may not be instantiated from an external 28 | * package and is effectively sealed. 29 | */ 30 | Resolver() {} 31 | 32 | /** 33 | * Defines a new procedure for handling a specific type of error. Acquire this handler 34 | * via {@link Result#resolve}. 35 | * 36 | * @param type The type of error being handled. 37 | * @param func A function consuming the specified error, if present. 38 | * @return The current Resolver which now contains the procedure. 39 | */ 40 | @CheckReturnValue 41 | @SuppressWarnings("unchecked") 42 | public Resolver resolve(final Class type, final Function func) { 43 | this.procedures.add((Procedure) new Procedure<>(type, func)); 44 | return this; 45 | } 46 | 47 | /** 48 | * Constructs a new Result from the supplied operation. Unlike other Result wrappers, 49 | * this operation is not computed lazily, as any potential errors will be handled 50 | * according to the protocol. 51 | * 52 | * @throws MissingProcedureException If a caught exception is undefined. 53 | * @throws NullPointerException If the attempt returns null. 54 | * @param attempt A function which either yields a value or throws an exception. 55 | * @return The result of the operation. 56 | */ 57 | @CheckReturnValue 58 | public Result.Value suppress(final ThrowingSupplier attempt) { 59 | T value; 60 | try { 61 | value = attempt.get(); 62 | } catch (Throwable e) { 63 | value = tryHandle(e); 64 | } 65 | return Result.ok(Objects.requireNonNull(value, "nonnull attempt")); 66 | } 67 | 68 | /** 69 | * Variant of {@link Protocol#suppress(ThrowingSupplier)} which is allowed to return null. 70 | * After defining procedures for this wrapper, the next ideal step is to call 71 | * {@link OptionalResult#defaultIfEmpty} to obtain an upgraded {@link PartialResult}. 72 | * 73 | * @param attempt A function which either yields a value, throws an exception, or 74 | * returns null. 75 | * @return A result which may either be a value, error, or empty. 76 | */ 77 | public OptionalResult nullable(final ThrowingSupplier attempt) { 78 | try { 79 | return Result.nullable(attempt.get()); 80 | } catch (Throwable e) { 81 | return Result.nullable(tryHandle(e)); 82 | } 83 | } 84 | 85 | /** 86 | * Variant of {@link Protocol#nullable(ThrowingSupplier)} in which the return value is 87 | * wrapped in {@link Optional}. 88 | * 89 | * @param attempt A function which either yields a value, throws an exception, or 90 | * returns null. 91 | * @return A result which may either be a value, error, or empty. 92 | */ 93 | public OptionalResult nullable(final ThrowingOptionalSupplier attempt) { 94 | return this.nullable(() -> attempt.get().orElse(null)); 95 | } 96 | 97 | /** 98 | * Variant of {@link #nullable(ThrowingSupplier)} which returns the value 99 | * directly, wrapped in {@link Optional}. 100 | * 101 | * @param attempt A function which either yields a value or throws an exception. 102 | * @return The value yielded by the operation, wrapped in {@link Optional}. 103 | */ 104 | @CheckReturnValue 105 | public Optional get(final ThrowingSupplier attempt) { 106 | return this.nullable(attempt).get(); 107 | } 108 | 109 | /** 110 | * Directly exposes the value resulting from this attempt, or else the output of 111 | * the defined handlers. 112 | * 113 | * @throws NullPointerException If the attempt returns null. 114 | * @param attempt A function which either yield a value or throws an exception. 115 | * @return The output of this function, or else one of the provided defaults. 116 | */ 117 | public T expose(final ThrowingSupplier attempt) { 118 | return this.suppress(attempt).expose(); 119 | } 120 | 121 | /** 122 | * Attempts to handle the input exception, returning whichever value was 123 | * returned by the handler. 124 | */ 125 | private T tryHandle(final Throwable e) { 126 | for (final Procedure proc : this.procedures) { 127 | if (proc.clazz.isInstance(e)) { 128 | return proc.func.apply(proc.clazz.cast(e)); 129 | } 130 | } 131 | throw missingProcedureEx(e); 132 | } 133 | 134 | @SuppressWarnings("unchecked") 135 | private E cast(final Throwable e) { 136 | return (E) e; 137 | } 138 | 139 | /** A procedure defined for handling a specific type of error. */ 140 | private static class Procedure { 141 | final Class clazz; 142 | final Function func; 143 | 144 | Procedure(final Class clazz, final Function func) { 145 | this.clazz = clazz; 146 | this.func = func; 147 | } 148 | } 149 | } 150 | -------------------------------------------------------------------------------- /src/main/java/personthecat/fresult/Protocol.java: -------------------------------------------------------------------------------- 1 | package personthecat.fresult; 2 | 3 | import org.jetbrains.annotations.CheckReturnValue; 4 | import personthecat.fresult.exception.MissingProcedureException; 5 | import personthecat.fresult.functions.ThrowingOptionalSupplier; 6 | import personthecat.fresult.functions.ThrowingRunnable; 7 | import personthecat.fresult.functions.ThrowingSupplier; 8 | 9 | import java.util.HashSet; 10 | import java.util.Objects; 11 | import java.util.Optional; 12 | import java.util.Set; 13 | import java.util.function.Consumer; 14 | 15 | import static personthecat.fresult.Shorthand.missingProcedureEx; 16 | 17 | /** 18 | * A set of procedures for handling specific error types. 19 | */ 20 | @SuppressWarnings({"unused", "UnusedReturnValue"}) 21 | public final class Protocol { 22 | 23 | private final Set> procedures = new HashSet<>(); 24 | 25 | /** 26 | * Indicates that this type may not be instantiated from an external 27 | * package and is effectively sealed. 28 | */ 29 | Protocol() {} 30 | 31 | /** 32 | * Defines a new procedure for handling a specific type of error. Acquire this handler 33 | * via {@link Result#define}. 34 | * 35 | * @param type The type of error being handled. 36 | * @param func A function consuming the specified error, if present. 37 | * @return The current Protocol which now contains the procedure. 38 | */ 39 | @CheckReturnValue 40 | public Protocol define(final Class type, final Consumer func) { 41 | this.procedures.add(new Procedure<>(type, func)); 42 | return this; 43 | } 44 | 45 | /** 46 | * Constructs a new Result from the supplied operation. Unlike other Result wrappers, 47 | * this operation is not computed lazily, as any potential errors will be handled 48 | * according to the protocol. 49 | * 50 | * @throws MissingProcedureException If a caught exception is undefined. 51 | * @throws NullPointerException If the attempt returns null. 52 | * @param attempt A function which either yields a value or throws an exception. 53 | * @param The type of value expected from this procedure. 54 | * @return The result of the operation. 55 | */ 56 | @CheckReturnValue 57 | public Result suppress(final ThrowingSupplier attempt) { 58 | final T value; 59 | try { 60 | value = attempt.get(); 61 | } catch (Throwable e) { 62 | if (tryHandle(e)) { 63 | return Result.err(e); 64 | } 65 | throw missingProcedureEx(e); 66 | } 67 | return Result.ok(Objects.requireNonNull(value, "nonnull attempt")); 68 | } 69 | 70 | /** 71 | * Variant of {@link #suppress(ThrowingSupplier)} which does not yield a value. 72 | * 73 | * @param attempt A function which may throw an exception. 74 | * @return The result of the operation. 75 | */ 76 | @CheckReturnValue 77 | public Result suppress(final ThrowingRunnable attempt) { 78 | return this.suppress(Result.wrapVoid(attempt)); 79 | } 80 | 81 | /** 82 | * Variant of {@link Protocol#suppress(ThrowingSupplier)} which is allowed to return null. 83 | * After defining procedures for this wrapper, the next ideal step is to call 84 | * {@link OptionalResult#defaultIfEmpty} to obtain an upgraded {@link PartialResult}. 85 | * 86 | * @param attempt A function which either yields a value, throws an exception, or 87 | * returns null. 88 | * @param The type of value expected from this procedure. 89 | * @return A result which may either be a value, error, or empty. 90 | */ 91 | public OptionalResult nullable(final ThrowingSupplier attempt) { 92 | try { 93 | return Result.nullable(attempt.get()); 94 | } catch (Throwable e) { 95 | if (tryHandle(e)) { 96 | return Result.err(e); 97 | } 98 | throw missingProcedureEx(e); 99 | } 100 | } 101 | 102 | /** 103 | * Variant of {@link Protocol#nullable} in which the return value is wrapped in 104 | * {@link Optional}. 105 | * 106 | * @param attempt A function which either yields a value, throws an exception, or 107 | * returns null. 108 | * @param The type of value expected from this procedure. 109 | * @return A result which may either be an optional value or an error. 110 | */ 111 | public OptionalResult nullable(final ThrowingOptionalSupplier attempt) { 112 | return this.nullable(() -> attempt.get().orElse(null)); 113 | } 114 | 115 | /** 116 | * Variant of {@link #nullable(ThrowingSupplier)} which returns the value directly, 117 | * wrapped in {@link Optional}. 118 | * 119 | * @param attempt A function which either yields a value or throws an exception. 120 | * @return The value yielded by the operation, wrapped in {@link Optional}. 121 | */ 122 | @CheckReturnValue 123 | public Optional get(final ThrowingSupplier attempt) { 124 | return this.nullable(attempt).get(); 125 | } 126 | 127 | /** 128 | * Variant of {@link #suppress(ThrowingRunnable)} which executes the procedure 129 | * immediately and returns no output. 130 | * 131 | * @throws MissingProcedureException If a caught exception is undefined. 132 | * @param attempt A function which either yields a value or throws an exception. 133 | */ 134 | public void run(final ThrowingRunnable attempt) { 135 | try { 136 | attempt.run(); 137 | } catch (Throwable e) { 138 | if (!tryHandle(e)) { 139 | throw missingProcedureEx(e); 140 | } 141 | } 142 | } 143 | 144 | /** 145 | * Attempts to handle the input exception, returning whether a procedure 146 | * is implemented. 147 | */ 148 | private boolean tryHandle(final Throwable e) { 149 | return this.procedures.stream().anyMatch(proc -> { 150 | if (proc.clazz.isInstance(e)) { 151 | proc.func.accept(cast(e)); 152 | return true; 153 | } 154 | return false; 155 | }); 156 | } 157 | 158 | @SuppressWarnings("unchecked") 159 | private E cast(final Throwable e) { 160 | return (E) e; 161 | } 162 | 163 | /** 164 | * A procedure defined for handling a specific type of error. 165 | */ 166 | private record Procedure(Class clazz, Consumer func) {} 167 | } 168 | -------------------------------------------------------------------------------- /src/main/java/personthecat/fresult/WithResource.java: -------------------------------------------------------------------------------- 1 | package personthecat.fresult; 2 | 3 | import personthecat.fresult.functions.ThrowingConsumer; 4 | import personthecat.fresult.functions.ThrowingFunction; 5 | import personthecat.fresult.functions.ThrowingOptionalFunction; 6 | import personthecat.fresult.functions.ThrowingSupplier; 7 | 8 | import java.util.Optional; 9 | 10 | /** 11 | * A helper classed used for generating Results with a single closeable resource. 12 | * Equivalent to using try-with-resources. 13 | * 14 | * @param The resource being consumed and closed by this handler. 15 | * @param The type of error to be caught by the handler. 16 | */ 17 | @SuppressWarnings({"unused", "UnusedReturnValue"}) 18 | public class WithResource { 19 | 20 | private final ThrowingSupplier rGetter; 21 | 22 | WithResource(final ThrowingSupplier rGetter) { 23 | this.rGetter = rGetter; 24 | } 25 | 26 | /** 27 | * Constructs a new Result, consuming the resource and yielding a new value. 28 | * Equivalent to calling {@link Result#of} after queueing resources. 29 | * This ultimately changes the type of error handled by the wrapper, but the 30 | * original error must be a subclass of the new error in order for it to work. 31 | * 32 | * @param attempt A function which consumes the resource and either returns a 33 | * value or throws an exception. 34 | * @param The type of value being returned by the wrapper. 35 | * @param The type of exception thrown by the resource getter. 36 | * @return A result which may either be a value or an error. 37 | */ 38 | public Result.Pending of(final ThrowingFunction attempt) { 39 | return Result.of(() -> this.execute(attempt)); 40 | } 41 | 42 | /** 43 | * Variant of {@link #of(ThrowingFunction)} which does not return a value. 44 | * 45 | * @param attempt A function which consumes the resource and may throw an 46 | * exception. 47 | * @param The type of exception thrown by the resource getter. 48 | * @return A result which may either be OK or an error. 49 | */ 50 | public Result.Pending of(final ThrowingConsumer attempt) { 51 | return this.of(Result.wrapVoid(attempt)); 52 | } 53 | 54 | /** 55 | * Variant of {@link #of(ThrowingFunction)} which implies that the type of 56 | * exception being thrown is not significant. For this reason, we ignore 57 | * the safeguards via type coercion and immediately return a {@link Result}. 58 | * 59 | * @param attempt A function which consumes the resource and either returns a 60 | * value or throws any type of exception. 61 | * @param The type of value being returned by the wrapper. 62 | * @param The type of exception thrown by the resource getter. 63 | * @return A result which may either be a value or any error. 64 | */ 65 | public Result suppress(final ThrowingFunction attempt) { 66 | return Result.suppress(() -> this.execute(attempt)); 67 | } 68 | 69 | /** 70 | * Variant of {@link #of(ThrowingConsumer)} which implies that the type 71 | * of exception being thrown is not significant. 72 | * 73 | * @see WithResource#suppress(ThrowingFunction) 74 | * @param attempt A function which consumes the resource any either returns a 75 | * value or throws any type of exception. 76 | * @param The type of exception thrown by the resource getter. 77 | * @return A result which may either be a value or any error. 78 | */ 79 | public Result suppress(final ThrowingConsumer attempt) { 80 | return this.suppress(Result.wrapVoid(attempt)); 81 | } 82 | 83 | /** 84 | * Variant of {@link WithResource#of} which is allowed to return a null value. 85 | * 86 | * @param attempt A function which consumes the resource and either returns a 87 | * value, throws an exception, or returns null. 88 | * @param The type of value being returned by the wrapper. 89 | * @param The type of exception thrown by the resource getter. 90 | * @return A result which may either be a value, error, or null. 91 | */ 92 | public PartialOptionalResult nullable(final ThrowingFunction attempt) { 93 | return Result.nullable(() -> execute(attempt)); 94 | } 95 | 96 | /** 97 | * Variant of {@link WithResource#nullable(ThrowingFunction)} in which the return value 98 | * is wrapped in {@link Optional}. 99 | * 100 | * @param attempt A function which consumes the resource and either returns a 101 | * value, throws an exception, or returns null. 102 | * @param The type of value being returned by the wrapper. 103 | * @param The type of exception thrown by the resource getter. 104 | * @return A result which may either be a value, error, or null. 105 | */ 106 | public PartialOptionalResult nullable(final ThrowingOptionalFunction attempt) { 107 | return this.nullable((ThrowingFunction) r -> attempt.apply(r).orElse(null)); 108 | } 109 | 110 | /** 111 | * Variant of {@link WithResource#nullable} which is allowed to throw any 112 | * kind of exception. 113 | * 114 | * @param attempt A function which consumes the resource and either returns a 115 | * value, throws any exception, or returns null. 116 | * @param The type of value being returned by the wrapper. 117 | * @param The type of exception thrown by the resource getter. 118 | * @return A result which may either be a value, any error, or null. 119 | */ 120 | public OptionalResult suppressNullable(final ThrowingFunction attempt) { 121 | return Result.suppressNullable(() -> execute(attempt)); 122 | } 123 | 124 | /** 125 | * Variant of {@link WithResource#suppressNullable(ThrowingFunction)} in which the return 126 | * value is wrapped in {@link Optional}. 127 | * 128 | * @param attempt A function which consumes the resource and either returns a 129 | * value, throws any exception, or returns null. 130 | * @param The type of value being returned by the wrapper. 131 | * @param The type of exception thrown by the resource getter. 132 | * @return A result which may either be a value, any error, or null. 133 | */ 134 | public OptionalResult suppressNullable(final ThrowingOptionalFunction attempt) { 135 | return this.suppressNullable((ThrowingFunction) r -> attempt.apply(r).orElse(null)); 136 | } 137 | 138 | /** 139 | * Constructs a new handler with an additional resource. 140 | * 141 | * @param resource A function which either supplies the intended resource or 142 | * throws an exception. 143 | * @return A new wrapper containing getters for both resources. 144 | */ 145 | public WithResources with(final ThrowingSupplier resource) { 146 | return new WithResources<>(rGetter, resource); 147 | } 148 | 149 | /** 150 | * Constructs a new handler with an additional resource yielded from the first. 151 | * 152 | * @param getter A function which accepts the first resource, yields the 153 | * second, and may throw an exception. 154 | * @return A new wrapper containing getters for both resources. 155 | */ 156 | public WithResources with(final ThrowingFunction getter) { 157 | return new WithResources<>(rGetter, () -> getter.apply(rGetter.get())); 158 | } 159 | 160 | /** 161 | * Executes the underlying procedure being wrapped by this utility. 162 | * 163 | * @throws E If an error occurs. 164 | * @param attempt The procedure to attempt running which consumes the resource. 165 | * @param The type of value being returned by the wrapper. 166 | * @param The type of exception thrown by the resource getter. 167 | * @return The value expected by the wrapper. 168 | */ 169 | private T execute(final ThrowingFunction attempt) throws E { 170 | try (R r = this.rGetter.get()) { 171 | return attempt.apply(r); 172 | } catch (Throwable e) { 173 | throw Shorthand.errorFound(e); 174 | } 175 | } 176 | } -------------------------------------------------------------------------------- /src/main/java/personthecat/fresult/PartialResult.java: -------------------------------------------------------------------------------- 1 | package personthecat.fresult; 2 | 3 | import org.jetbrains.annotations.CheckReturnValue; 4 | import personthecat.fresult.exception.ResultUnwrapException; 5 | import personthecat.fresult.exception.WrongErrorException; 6 | import personthecat.fresult.functions.ThrowingFunction; 7 | 8 | import java.util.Optional; 9 | import java.util.function.Consumer; 10 | import java.util.function.Function; 11 | import java.util.function.Supplier; 12 | 13 | import static personthecat.fresult.Shorthand.f; 14 | 15 | @SuppressWarnings({"unused", "UnusedReturnValue"}) 16 | public sealed interface PartialResult 17 | extends PartialOptionalResult 18 | permits Result.Value, Result.Error, Result.Pending { 19 | 20 | /** 21 | * Variant of {@link Result#isErr} which can safely guarantee whether the 22 | * expected type of error is present in the wrapper. If no error is present, this 23 | * function yields false. If the expected error is present, this function 24 | * yields true. If an unexpected error is found, it will be thrown. 25 | * 26 | * @throws WrongErrorException If any other type of exception is found. 27 | * @param clazz The type of error expected by the wrapper. 28 | * @return Whether an expected type of exception is found. 29 | */ 30 | @Override 31 | @CheckReturnValue 32 | boolean isErr(final Class clazz); 33 | 34 | /** 35 | * Variant of {@link Result#isErr} which provides a semantic contract that the 36 | * type of error being handled is not significant. 37 | * 38 | * @return Whether any error is found in the wrapper. 39 | */ 40 | @Override 41 | @CheckReturnValue 42 | boolean isAnyErr(); 43 | 44 | /** 45 | * Accepts an expression for what to do in the event of an error being present. 46 | *

47 | * Use this whenever you want to functionally handle both code paths (i.e. error 48 | * vs. value). 49 | *

50 | * @throws WrongErrorException If the underlying error is an unexpected type. 51 | * @param f A function consuming the error, if present. 52 | * @return This, or else a complete {@link Result}. 53 | */ 54 | @Override 55 | Result ifErr(final Consumer f); 56 | 57 | /** 58 | * Retrieves the underlying error, wrapped in Optional. 59 | * 60 | * @return The underlying error, wrapped in {@link Optional}. 61 | */ 62 | @Override 63 | @CheckReturnValue 64 | Optional getAnyErr(); 65 | 66 | /** 67 | * Converts this wrapper into a new type when accounting for both potential outcomes. 68 | * 69 | * @param ifOk The transformer applied if a value is present in the wrapper. 70 | * @param ifErr The transformer applied if an error is present in the wrapper. 71 | * @param The type of value being output by this method. 72 | * @return The output of either ifOk or ifErr. 73 | */ 74 | @CheckReturnValue 75 | U fold(final Function ifOk, final Function ifErr); 76 | 77 | /** 78 | * Strips away any uncertainty surrounding whether this wrapper contains an unexpected 79 | * exception or otherwise and returns a definite {@link Result.Value}. 80 | * 81 | *

e.g.

82 | *
 83 |      *   final String value = Result.suppress(() -> "Hello, world!")
 84 |      *     .resolve(e -> "Default value")
 85 |      *     .expose(); // No consequences.
 86 |      * 
87 | * 88 | * @param ifErr A handler which provides a default value. 89 | * @return A result which can only be a {@link Result.Value}. 90 | */ 91 | @CheckReturnValue 92 | Result.Value resolve(final Function ifErr); 93 | 94 | /** 95 | * Variant of {@link Result#get()} which simultaneously handles a potential error 96 | * and yields a substitute value. Equivalent to following a {@link Result#get()} 97 | * call with a call to {@link Optional#orElseGet}. 98 | * 99 | * @throws RuntimeException wrapping the original error, if an unexpected type 100 | * of error is present in the wrapper. 101 | * @param f A function which returns an alternate value given the error, if 102 | * present. 103 | * @return The underlying value, or else func.apply(E). 104 | */ 105 | @CheckReturnValue 106 | T orElseGet(final Function f); 107 | 108 | /** 109 | * Maps to a new Result if an error is present. Use this whenever your first and 110 | * second attempt at retrieving a value may fail. 111 | * 112 | * @param f A new function to attempt in the presence of an error. 113 | * @return The pending result of the new function, if an error is present, 114 | * or else a complete {@link Result}. 115 | */ 116 | @CheckReturnValue 117 | PartialResult orElseTry(final ThrowingFunction f); 118 | 119 | /** 120 | * Variant of {@link #orElseTry(ThrowingFunction)} which handles the error when 121 | * given a specific {@link Protocol}. 122 | * 123 | * @param protocol Instructions for handling errors beyond this point. 124 | * @param f A new function to attempt in the presence of an error. 125 | * @return A result which may contain either a value or any error. 126 | */ 127 | @CheckReturnValue 128 | Result orElseTry(final Protocol protocol, final ThrowingFunction f); 129 | 130 | /** 131 | * Variant of {@link #orElseTry(ThrowingFunction)} which handles the error when 132 | * given a {@link Resolver}. 133 | * 134 | * @param resolver Instructions for how to resolve a value in the presence of 135 | * an error. 136 | * @param f A new function to attempt in the presence of an error. 137 | * @return A result which can only be a value. 138 | */ 139 | @CheckReturnValue 140 | Result.Value orElseTry(final Resolver resolver, final ThrowingFunction f); 141 | 142 | /** 143 | * Attempts to retrieve the underlying error, asserting that one must exist. 144 | * 145 | * @throws ResultUnwrapException If no error is present to be unwrapped. 146 | * @return The underlying error. 147 | */ 148 | @Override 149 | @CheckReturnValue 150 | default Throwable unwrapErr() { 151 | return this.expectErr("Attempted to unwrap a result with no error."); 152 | } 153 | 154 | /** 155 | * Returns a generic type of {@link Throwable} which could be any exception. 156 | * 157 | *

158 | * You may wish to use this method instead of {@link Result#expectErr} as it is 159 | * guaranteed to have a valid output and will never result in a {@link ClassCastException}. 160 | *

161 | * 162 | * @throws ResultUnwrapException If no error is present to be unwrapped. 163 | * @param message The message to display in the event of an error. 164 | * @return The underlying error. 165 | */ 166 | @Override 167 | Throwable expectErr(final String message); 168 | 169 | /** 170 | * @see PartialOptionalResult#expectErr(String) 171 | * @throws ResultUnwrapException If no error is present to be unwrapped. 172 | * @param message The message to display in the event of an error. 173 | * @param args A series of interpolated arguments (replacing {}). 174 | * @return The underlying error. 175 | */ 176 | @Override 177 | default Throwable expectErr(final String message, final Object... args) { 178 | return expectErr(f(message, args)); 179 | } 180 | 181 | /** 182 | * Variant of {@link #unwrap} which throws the original error, if applicable. 183 | * 184 | * @throws E The original error, if present. 185 | * @return The underlying value. 186 | */ 187 | @Override 188 | default T orElseThrow() throws Throwable { 189 | this.throwIfErr(); 190 | return this.unwrap(); 191 | } 192 | 193 | /** 194 | * If an error is present, it will be thrown by this method. 195 | * 196 | * @throws E The original error, if present. 197 | */ 198 | @Override 199 | void throwIfErr() throws Throwable; 200 | 201 | /** 202 | * @deprecated Never empty. 203 | */ 204 | @Override 205 | @Deprecated 206 | default PartialResult defaultIfEmpty(final Supplier defaultGetter) { 207 | return this; 208 | } 209 | 210 | /** 211 | * @deprecated Never empty. 212 | */ 213 | @Override 214 | @Deprecated 215 | default PartialResult ifEmpty(final Runnable f) { 216 | return this; 217 | } 218 | 219 | /** 220 | * @deprecated Never empty. 221 | */ 222 | @Override 223 | @Deprecated 224 | default void assertEmpty() {} 225 | 226 | /** 227 | * @deprecated Never empty. 228 | */ 229 | @Override 230 | @Deprecated 231 | default void expectEmpty(final String message) {} 232 | 233 | /** 234 | * @deprecated Never empty. 235 | */ 236 | @Override 237 | @Deprecated 238 | default void expectEmpty(final String message, final Object... args) {} 239 | } 240 | -------------------------------------------------------------------------------- /src/main/java/personthecat/fresult/PartialOptionalResult.java: -------------------------------------------------------------------------------- 1 | package personthecat.fresult; 2 | 3 | import personthecat.fresult.exception.ResultUnwrapException; 4 | import personthecat.fresult.exception.WrongErrorException; 5 | 6 | import org.jetbrains.annotations.CheckReturnValue; 7 | import java.util.Optional; 8 | import java.util.function.Consumer; 9 | import java.util.function.Supplier; 10 | 11 | import static personthecat.fresult.Shorthand.f; 12 | 13 | /** 14 | * The parent type of all possible {@link Result} values. 15 | * 16 | *

17 | * The OptionalResult type represents the least amount of certainty possible 18 | * for any given outcome. It implies that a procedure has terminated in one of three 19 | * potential states: 20 | *

21 | *
    22 | *
  • A value,
  • 23 | *
  • An error, or
  • 24 | *
  • null.
  • 25 | *
26 | *

27 | * As a result, this type provides the smallest set of functional utilities for interacting 28 | * with the data. To achieve a higher level of certainty about the state of this wrapper, 29 | * you can begin by stripping away the layers uncertainty, starting in most cases with 30 | * nullability. 31 | * 32 | * In general, nullability is the first layer to be stripped away. This can be accomplished 33 | * by supplying a default value via {@link PartialOptionalResult#defaultIfEmpty}, which in turn 34 | * yields a {@link PartialResult}. 35 | *

36 | *
 37 |  *   final PartialResult<String, RuntimeException> result = Result.nullable(() -> null)
 38 |  *     .defaultIfEmpty(() -> "Hello, World!");
 39 |  * 
40 | *

41 | * Alternatively, you may wish to handle this scenario by some other means. This can be 42 | * accomplished by calling {@link PartialOptionalResult#ifEmpty}. 43 | *

44 | *
 45 |  *   final OptionalResult<String, RuntimeException> result = Result.nullable(() -> null)
 46 |  *     .ifEmpty(() -> {...});
 47 |  * 
48 | *

49 | * Finally, FResult also provides utilities for terminating early if an error or null is 50 | * present ({@link #expect}, {@link #unwrap}, etc.). Let's take a look at the quickest way 51 | * to stop early if anything goes wrong: 52 | *

53 | *
 54 |  *   final String result = Result.<String, RuntimeException>nullable(() -> null)
 55 |  *     .expect("Error message!");
 56 |  * 
57 | * 58 | * @see PartialResult 59 | * @param The type of data being consumed by the wrapper. 60 | * @param The type of error being consumed by the wrapper. 61 | */ 62 | @SuppressWarnings({"unused", "UnusedReturnValue"}) 63 | public sealed interface PartialOptionalResult 64 | extends BasicResult 65 | permits Result.Empty, Result.Error, PartialResult, Result.Pending, Result.PendingNullable, Result.Value { 66 | 67 | /** 68 | * Consumes instructions for what to do if a given result contains no value. This 69 | * method eliminates any uncertainty surrounding whether null values can exist within 70 | * the wrapper. As a result, its type will be upgraded to a more permissive 71 | * {@link PartialResult}. 72 | * 73 | * @param defaultGetter Supplies a default value if the wrapper does not contain one. 74 | * @return An upgraded {@link PartialResult} for handling unknown error types. 75 | */ 76 | PartialResult defaultIfEmpty(final Supplier defaultGetter); 77 | 78 | /** 79 | * Generates an error in the case where no value is present at all in this wrapper. 80 | * 81 | *

As with {@link #defaultIfEmpty(Supplier)}, this method eliminates the possibility 82 | * of empty state, thus allowing it to return a standard, {@link PartialResult partial 83 | * result}. 84 | * 85 | * @param defaultGetter Supplies an error if the wrapper contains no value. 86 | * @return An upgraded {@link PartialResult} for handling unknown error types. 87 | */ 88 | PartialResult errIfEmpty(final Supplier defaultGetter); 89 | 90 | /** 91 | * Consumes a procedure to be executed if no value is present in the wrapper. 92 | * 93 | * @param f A function to run if this wrapper contains no value. 94 | * @return This, or else an upgraded {@link PartialResult}. 95 | */ 96 | PartialOptionalResult ifEmpty(final Runnable f); 97 | 98 | /** 99 | * Variant of {@link Result#isErr} which can safely guarantee whether the 100 | * expected type of error is present in the wrapper. If no error is present, this 101 | * function yields false. If the expected error is present, this function 102 | * yields true. If an unexpected error is found, it will be thrown. 103 | * 104 | * @throws WrongErrorException If any other type of exception is found. 105 | * @param clazz The type of error expected by the wrapper. 106 | * @return Whether an expected type of exception is found. 107 | */ 108 | @CheckReturnValue 109 | boolean isErr(final Class clazz); 110 | 111 | /** 112 | * Variant of {@link Result#isErr} which provides a semantic contract that the 113 | * type of error being handled is not significant. 114 | * 115 | * @return Whether any error is found in the wrapper. 116 | */ 117 | @CheckReturnValue 118 | boolean isAnyErr(); 119 | 120 | /** 121 | * Accepts an expression for what to do in the event of an error being present. 122 | *

123 | * Use this whenever you want to functionally handle both code paths (i.e. error 124 | * vs. value). 125 | *

126 | * @throws WrongErrorException If the underlying error is an unexpected type. 127 | * @param f A function consuming the error, if present. 128 | * @return This, or else a complete {@link Result}. 129 | */ 130 | OptionalResult ifErr(final Consumer f); 131 | 132 | /** 133 | * Retrieves the underlying error, wrapped in Optional. 134 | * 135 | * @return The underlying error, wrapped in {@link Optional}. 136 | */ 137 | @CheckReturnValue 138 | Optional getAnyErr(); 139 | 140 | /** 141 | * Attempts to retrieve the underlying error, asserting that one must exist. 142 | * 143 | * @throws ResultUnwrapException If no error is present to be unwrapped. 144 | * @return The underlying error. 145 | */ 146 | @CheckReturnValue 147 | default Throwable unwrapErr() { 148 | return expectErr("Attempted to unwrap a result with no error."); 149 | } 150 | 151 | /** 152 | * Asserts that no value or error will be present within the wrapper. 153 | * 154 | * @throws ResultUnwrapException If a value or an error is present. 155 | */ 156 | default void assertEmpty() { 157 | this.expectEmpty("Wrapper contains a value or error."); 158 | } 159 | 160 | /** 161 | * Asserts that no value or error will be present within the wrapper. 162 | * 163 | * @param message The message to display in the event of an error. 164 | * @throws ResultUnwrapException If a value or an error is present. 165 | */ 166 | void expectEmpty(final String message); 167 | 168 | /** 169 | * Formatted variant of {@link #expectEmpty}. 170 | * 171 | * @param message The message to display in the event of an error. 172 | * @param args A series of interpolated arguments (replacing {}). 173 | * @throws ResultUnwrapException If a value or an error is present. 174 | */ 175 | default void expectEmpty(final String message, final Object... args) { 176 | this.expectEmpty(f(message, args)); 177 | } 178 | 179 | /** 180 | * Returns a generic type of {@link Throwable} which could be any exception. 181 | * 182 | *

183 | * You may wish to use this method instead of {@link Result#expectErr} as it is 184 | * guaranteed to have a valid output and will never result in a {@link ClassCastException}. 185 | *

186 | * 187 | * @throws ResultUnwrapException If no error is present to be unwrapped. 188 | * @param message The message to display in the event of an error. 189 | * @return The underlying error. 190 | */ 191 | Throwable expectErr(final String message); 192 | 193 | /** 194 | * @see PartialOptionalResult#expectErr(String) 195 | * @throws ResultUnwrapException If no error is present to be unwrapped. 196 | * @param message The message to display in the event of an error. 197 | * @param args A series of interpolated arguments (replacing {}). 198 | * @return The underlying error. 199 | */ 200 | default Throwable expectErr(final String message, final Object... args) { 201 | return this.expectErr(f(message, args)); 202 | } 203 | 204 | /** 205 | * Variant of {@link #unwrap} which throws the original error, if applicable. 206 | * 207 | * @throws E The original error, if present. 208 | * @return The underlying value. 209 | */ 210 | default T orElseThrow() throws Throwable { 211 | this.throwIfErr(); 212 | return this.unwrap(); 213 | } 214 | 215 | /** 216 | * If an error is present, it will be thrown by this method. 217 | * 218 | * @throws E The original error, if present. 219 | */ 220 | void throwIfErr() throws Throwable; 221 | } 222 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # 4 | # Copyright © 2015-2021 the original authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | ############################################################################## 20 | # 21 | # Gradle start up script for POSIX generated by Gradle. 22 | # 23 | # Important for running: 24 | # 25 | # (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is 26 | # noncompliant, but you have some other compliant shell such as ksh or 27 | # bash, then to run this script, type that shell name before the whole 28 | # command line, like: 29 | # 30 | # ksh Gradle 31 | # 32 | # Busybox and similar reduced shells will NOT work, because this script 33 | # requires all of these POSIX shell features: 34 | # * functions; 35 | # * expansions «$var», «${var}», «${var:-default}», «${var+SET}», 36 | # «${var#prefix}», «${var%suffix}», and «$( cmd )»; 37 | # * compound commands having a testable exit status, especially «case»; 38 | # * various built-in commands including «command», «set», and «ulimit». 39 | # 40 | # Important for patching: 41 | # 42 | # (2) This script targets any POSIX shell, so it avoids extensions provided 43 | # by Bash, Ksh, etc; in particular arrays are avoided. 44 | # 45 | # The "traditional" practice of packing multiple parameters into a 46 | # space-separated string is a well documented source of bugs and security 47 | # problems, so this is (mostly) avoided, by progressively accumulating 48 | # options in "$@", and eventually passing that to Java. 49 | # 50 | # Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, 51 | # and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; 52 | # see the in-line comments for details. 53 | # 54 | # There are tweaks for specific operating systems such as AIX, CygWin, 55 | # Darwin, MinGW, and NonStop. 56 | # 57 | # (3) This script is generated from the Groovy template 58 | # https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt 59 | # within the Gradle project. 60 | # 61 | # You can find Gradle at https://github.com/gradle/gradle/. 62 | # 63 | ############################################################################## 64 | 65 | # Attempt to set APP_HOME 66 | 67 | # Resolve links: $0 may be a link 68 | app_path=$0 69 | 70 | # Need this for daisy-chained symlinks. 71 | while 72 | APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path 73 | [ -h "$app_path" ] 74 | do 75 | ls=$( ls -ld "$app_path" ) 76 | link=${ls#*' -> '} 77 | case $link in #( 78 | /*) app_path=$link ;; #( 79 | *) app_path=$APP_HOME$link ;; 80 | esac 81 | done 82 | 83 | # This is normally unused 84 | # shellcheck disable=SC2034 85 | APP_BASE_NAME=${0##*/} 86 | APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit 87 | 88 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 89 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 90 | 91 | # Use the maximum available, or set MAX_FD != -1 to use that value. 92 | MAX_FD=maximum 93 | 94 | warn () { 95 | echo "$*" 96 | } >&2 97 | 98 | die () { 99 | echo 100 | echo "$*" 101 | echo 102 | exit 1 103 | } >&2 104 | 105 | # OS specific support (must be 'true' or 'false'). 106 | cygwin=false 107 | msys=false 108 | darwin=false 109 | nonstop=false 110 | case "$( uname )" in #( 111 | CYGWIN* ) cygwin=true ;; #( 112 | Darwin* ) darwin=true ;; #( 113 | MSYS* | MINGW* ) msys=true ;; #( 114 | NONSTOP* ) nonstop=true ;; 115 | esac 116 | 117 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 118 | 119 | 120 | # Determine the Java command to use to start the JVM. 121 | if [ -n "$JAVA_HOME" ] ; then 122 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 123 | # IBM's JDK on AIX uses strange locations for the executables 124 | JAVACMD=$JAVA_HOME/jre/sh/java 125 | else 126 | JAVACMD=$JAVA_HOME/bin/java 127 | fi 128 | if [ ! -x "$JAVACMD" ] ; then 129 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 130 | 131 | Please set the JAVA_HOME variable in your environment to match the 132 | location of your Java installation." 133 | fi 134 | else 135 | JAVACMD=java 136 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 137 | 138 | Please set the JAVA_HOME variable in your environment to match the 139 | location of your Java installation." 140 | fi 141 | 142 | # Increase the maximum file descriptors if we can. 143 | if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then 144 | case $MAX_FD in #( 145 | max*) 146 | # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. 147 | # shellcheck disable=SC3045 148 | MAX_FD=$( ulimit -H -n ) || 149 | warn "Could not query maximum file descriptor limit" 150 | esac 151 | case $MAX_FD in #( 152 | '' | soft) :;; #( 153 | *) 154 | # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. 155 | # shellcheck disable=SC3045 156 | ulimit -n "$MAX_FD" || 157 | warn "Could not set maximum file descriptor limit to $MAX_FD" 158 | esac 159 | fi 160 | 161 | # Collect all arguments for the java command, stacking in reverse order: 162 | # * args from the command line 163 | # * the main class name 164 | # * -classpath 165 | # * -D...appname settings 166 | # * --module-path (only if needed) 167 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. 168 | 169 | # For Cygwin or MSYS, switch paths to Windows format before running java 170 | if "$cygwin" || "$msys" ; then 171 | APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) 172 | CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) 173 | 174 | JAVACMD=$( cygpath --unix "$JAVACMD" ) 175 | 176 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 177 | for arg do 178 | if 179 | case $arg in #( 180 | -*) false ;; # don't mess with options #( 181 | /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath 182 | [ -e "$t" ] ;; #( 183 | *) false ;; 184 | esac 185 | then 186 | arg=$( cygpath --path --ignore --mixed "$arg" ) 187 | fi 188 | # Roll the args list around exactly as many times as the number of 189 | # args, so each arg winds up back in the position where it started, but 190 | # possibly modified. 191 | # 192 | # NB: a `for` loop captures its iteration list before it begins, so 193 | # changing the positional parameters here affects neither the number of 194 | # iterations, nor the values presented in `arg`. 195 | shift # remove old arg 196 | set -- "$@" "$arg" # push replacement arg 197 | done 198 | fi 199 | 200 | # Collect all arguments for the java command; 201 | # * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of 202 | # shell script including quotes and variable substitutions, so put them in 203 | # double quotes to make sure that they get re-expanded; and 204 | # * put everything else in single quotes, so that it's not re-expanded. 205 | 206 | set -- \ 207 | "-Dorg.gradle.appname=$APP_BASE_NAME" \ 208 | -classpath "$CLASSPATH" \ 209 | org.gradle.wrapper.GradleWrapperMain \ 210 | "$@" 211 | 212 | # Stop when "xargs" is not available. 213 | if ! command -v xargs >/dev/null 2>&1 214 | then 215 | die "xargs is not available" 216 | fi 217 | 218 | # Use "xargs" to parse quoted args. 219 | # 220 | # With -n1 it outputs one arg per line, with the quotes and backslashes removed. 221 | # 222 | # In Bash we could simply go: 223 | # 224 | # readarray ARGS < <( xargs -n1 <<<"$var" ) && 225 | # set -- "${ARGS[@]}" "$@" 226 | # 227 | # but POSIX shell has neither arrays nor command substitution, so instead we 228 | # post-process each arg (as a line of input to sed) to backslash-escape any 229 | # character that might be a shell metacharacter, then use eval to reverse 230 | # that process (while maintaining the separation between arguments), and wrap 231 | # the whole thing up as a single "set" statement. 232 | # 233 | # This will of course break if any of these variables contains a newline or 234 | # an unmatched quote. 235 | # 236 | 237 | eval "set -- $( 238 | printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | 239 | xargs -n1 | 240 | sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | 241 | tr '\n' ' ' 242 | )" '"$@"' 243 | 244 | exec "$JAVACMD" "$@" 245 | -------------------------------------------------------------------------------- /src/main/java/personthecat/fresult/WithResources.java: -------------------------------------------------------------------------------- 1 | package personthecat.fresult; 2 | 3 | import personthecat.fresult.functions.*; 4 | 5 | import java.util.Optional; 6 | 7 | /** 8 | * A helper classed used for generating Results with two closeable resources. 9 | * Equivalent to using try-with-resources. 10 | * 11 | * @param The resource being consumed and closed by this handler. 12 | * @param The second resource being consumed and closed by this handler. 13 | * @param The type of error to be caught by the handler. 14 | */ 15 | @SuppressWarnings("unused") 16 | public class WithResources { 17 | 18 | private final ThrowingSupplier r1Getter; 19 | private final ThrowingSupplier r2Getter; 20 | 21 | WithResources(final ThrowingSupplier r1Getter, final ThrowingSupplier r2Getter) { 22 | this.r1Getter = r1Getter; 23 | this.r2Getter = r2Getter; 24 | } 25 | 26 | /** 27 | * Constructs a new Result, consuming the resources and yielding a new value. 28 | * Equivalent to calling {@link Result#of} after queueing resources. 29 | * 30 | *

31 | * This ultimately changes the type of error handled by the wrapper, but the 32 | * original error must be a subclass of the new error in order for it to work. 33 | *

34 | * 35 | * @param attempt A function which consumes both resources and either returns a 36 | * value or throws an exception. 37 | * @param The type of value being consumed by the wrapper. 38 | * @param The type of error being consumed by the wrapper. 39 | * @return A result which may either be a value or an error. 40 | */ 41 | public Result.Pending of(final ThrowingBiFunction attempt) { 42 | return Result.of(() -> this.execute(attempt)); 43 | } 44 | 45 | /** 46 | * Variant of {@link #of(ThrowingBiFunction)} which does not return a value. 47 | * 48 | * @param attempt A function which consumes both values and may throw an exception. 49 | * @param The type of error being consumed by the wrapper. 50 | * @return A result which may either be OK or an error. 51 | */ 52 | public Result.Pending of(final ThrowingBiConsumer attempt) { 53 | return this.of(Result.wrapVoid(attempt)); 54 | } 55 | 56 | /** 57 | * Variant of {@link #of(ThrowingBiFunction)} which only accepts the second resource 58 | * in the queue. 59 | * 60 | * @param attempt A function which consumes the second resource and either returns a 61 | * value or throws an exception. 62 | * @param The type of value being consumed by the wrapper. 63 | * @param The type of error being consumed by the wrapper. 64 | * @return A result which may either be a value or an error. 65 | */ 66 | public Result.Pending of(final ThrowingFunction attempt) { 67 | return this.of((R1 r1, R2 r2) -> attempt.apply(r2)); 68 | } 69 | 70 | /** 71 | * Variant of {@link #of(ThrowingBiConsumer)} which only accepts the second resource in 72 | * the queue. 73 | * 74 | * @param attempt A function which consumes the second value and may throw an exception. 75 | * @param The type of error being consumed by the wrapper. 76 | * @return A result which may either OK or an error. 77 | */ 78 | public Result.Pending of(final ThrowingConsumer attempt) { 79 | return this.of((r1, r2) -> { attempt.accept(r2); }); 80 | } 81 | 82 | /** 83 | * Variant of {@link #of(ThrowingBiFunction)} which which implies that the type of 84 | * exception being thrown is not significant. For this reason, we ignore the safeguards 85 | * via type coercion and immediately return a {@link Result}. 86 | * 87 | * @param attempt A function which consumes both resources and either returns a value 88 | * or throws any type of exception. 89 | * @param The type of value being consumed by the wrapper. 90 | * @param The type of error being consumed by the wrapper. 91 | * @return A result which may either be a value or an any error. 92 | */ 93 | public Result suppress(final ThrowingBiFunction attempt) { 94 | return Result.suppress(() -> this.execute(attempt)); 95 | } 96 | 97 | /** 98 | * Variant of {@link #of(ThrowingBiFunction)} which implies that the type of exception 99 | * being thrown is not significant. 100 | * 101 | * @see WithResources#suppress(ThrowingBiFunction) 102 | * @param attempt A function which consumes both values and may throw an exception. 103 | * @param The type of error being consumed by the wrapper. 104 | * @return A result which may either be OK or any error. 105 | */ 106 | public Result suppress(final ThrowingBiConsumer attempt) { 107 | return this.suppress(Result.wrapVoid(attempt)); 108 | } 109 | 110 | /** 111 | * Variant of {@link #of(ThrowingFunction)} which implies that the type of exception 112 | * being thrown is not significant. 113 | * 114 | * @see WithResources#suppress(ThrowingBiFunction) 115 | * @param The type of value being consumed by the wrapper. 116 | * @param The type of error being consumed by the wrapper. 117 | * @return A result which may either be a value or any error. 118 | */ 119 | public Result suppress(final ThrowingFunction attempt) { 120 | return this.suppress((R1 r1, R2 r2) -> attempt.apply(r2)); 121 | } 122 | 123 | /** 124 | * Variant of {@link #of(ThrowingConsumer)} which implies that the type of exception 125 | * being thrown is not significant. 126 | * 127 | * @see WithResources#suppress(ThrowingBiFunction) 128 | * @param attempt A function which consumes the second value and may throw an exception. 129 | * @param The type of error being consumed by the wrapper. 130 | * @return A result which may either OK or an error. 131 | */ 132 | public Result suppress(final ThrowingConsumer attempt) { 133 | return this.suppress((r1, r2) -> { attempt.accept(r2); }); 134 | } 135 | 136 | /** 137 | * Variant of {@link WithResources#of(ThrowingBiFunction)} which is allowed to return 138 | * null. 139 | * 140 | * @param attempt A function which consumes both resources and either returns a value, 141 | * throws an exception, or returns null. 142 | * @param The type of value being consumed by the wrapper. 143 | * @param The type of error being consumed by the wrapper. 144 | * @return A result which may either be a value, an error, or null. 145 | */ 146 | public PartialOptionalResult nullable(final ThrowingBiFunction attempt) { 147 | return Result.nullable(() -> this.execute(attempt)); 148 | } 149 | 150 | /** 151 | * Variant of {@link WithResources#of(ThrowingFunction)} which is allowed to return 152 | * null. 153 | * 154 | * @param attempt A function which consumes the first resource and either returns a 155 | * value, throws an exception, or returns null. 156 | * @param The type of value being consumed by the wrapper. 157 | * @param The type of error being consumed by the wrapper. 158 | * @return A result which may either be a value, an error, or null. 159 | */ 160 | public PartialOptionalResult nullable(final ThrowingFunction attempt) { 161 | return this.nullable((R1 r1, R2 r2) -> attempt.apply(r2)); 162 | } 163 | 164 | /** 165 | * Variant of {@link WithResources#nullable(ThrowingBiFunction)} which is allowed to 166 | * throw any kind of exception. 167 | * 168 | * @param attempt A function which consumes both resources and either returns a value, 169 | * throws any exception, or returns null. 170 | * @param The type of value being consumed by the wrapper. 171 | * @param The type of error being consumed by the wrapper. 172 | * @return A result which may either be a value, any error, or null. 173 | */ 174 | public OptionalResult suppressNullable(final ThrowingBiFunction attempt) { 175 | return Result.suppressNullable(() -> this.execute(attempt)); 176 | } 177 | 178 | /** 179 | * Variant of {@link WithResources#nullable(ThrowingFunction)} which is allowed to 180 | * throw any kind of exception. 181 | * 182 | * @param attempt A function which consumes the first resource and either returns a 183 | * value, throws any exception, or returns null. 184 | * @param The type of value being consumed by the wrapper. 185 | * @param The type of error being consumed by the wrapper. 186 | * @return A result which may either be a value, any error, or null. 187 | */ 188 | public OptionalResult suppressNullable(final ThrowingFunction attempt) { 189 | return this.suppressNullable((R1 r1, R2 r2) -> attempt.apply(r2)); 190 | } 191 | 192 | /** 193 | * Variant of {@link WithResources#nullable(ThrowingBiFunction)} in which the return 194 | * value is wrapped in {@link Optional}. 195 | * 196 | * @param attempt A function which consumes both resources and either returns a value, 197 | * throws an exception, or returns null. 198 | * @param The type of value being consumed by the wrapper. 199 | * @param The type of error being consumed by the wrapper. 200 | * @return A result which may either be a value, an error, or null. 201 | */ 202 | public PartialOptionalResult nullable(final ThrowingOptionalBiFunction attempt) { 203 | return this.nullable((ThrowingBiFunction) (r1, r2) -> attempt.apply(r1, r2).orElse(null)); 204 | } 205 | 206 | /** 207 | * Variant of {@link WithResources#nullable(ThrowingFunction)} in which the return 208 | * value is wrapped in {@link Optional}. 209 | * 210 | * @param attempt A function which consumes the first resource and either returns a 211 | * value, throws an exception, or returns null. 212 | * @param The type of value being consumed by the wrapper. 213 | * @param The type of error being consumed by the wrapper. 214 | * @return A result which may either be a value, an error, or null. 215 | */ 216 | public PartialOptionalResult nullable(final ThrowingOptionalFunction attempt) { 217 | return this.nullable((R1 r1, R2 r2) -> attempt.apply(r2)); 218 | } 219 | 220 | /** 221 | * Variant of {@link WithResources#suppressNullable(ThrowingBiFunction)} in which 222 | * the return value is wrapped in {@link Optional}. 223 | * 224 | * @param attempt A function which consumes both resources and either returns a value, 225 | * throws any exception, or returns null. 226 | * @param The type of value being consumed by the wrapper. 227 | * @param The type of error being consumed by the wrapper. 228 | * @return A result which may either be a value, any error, or null. 229 | */ 230 | public OptionalResult suppressNullable(final ThrowingOptionalBiFunction attempt) { 231 | return this.suppressNullable((ThrowingBiFunction) (r1, r2) -> attempt.apply(r1, r2).orElse(null)); 232 | } 233 | 234 | /** 235 | * Variant of {@link WithResources#suppressNullable(ThrowingFunction)} in which the 236 | * return value is wrapped in {@link Optional}. 237 | * 238 | * @param attempt A function which consumes the first resource and either returns a 239 | * value, throws any exception, or returns null. 240 | * @param The type of value being consumed by the wrapper. 241 | * @param The type of error being consumed by the wrapper. 242 | * @return A result which may either be a value, any error, or null. 243 | */ 244 | public OptionalResult suppressNullable(final ThrowingOptionalFunction attempt) { 245 | return this.suppressNullable((R1 r1, R2 r2) -> attempt.apply(r2)); 246 | } 247 | 248 | /** 249 | * Executes the underlying procedure being wrapped by this utility. 250 | * 251 | * @throws E If an error occurs. 252 | * @param attempt The procedure to attempt running which consumes the resource. 253 | * @param The type of value being returned by the wrapper. 254 | * @param The type of exception thrown by the resource getter. 255 | * @return The value expected by the wrapper. 256 | */ 257 | private T execute(final ThrowingBiFunction attempt) throws E { 258 | try (R1 r1 = this.r1Getter.get(); R2 r2 = this.r2Getter.get()) { 259 | return attempt.apply(r1, r2); 260 | } catch (Throwable e) { 261 | throw Shorthand.errorFound(e); 262 | } 263 | } 264 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # FResult 2 | #### A unified type for optional and error-prone procedures in Java. 3 | 4 | FResult is a powerful and expressive counterpart to `java.util.Optional` used for 5 | neatly handling errors. You can suppress all errors and immediately get a value: 6 | 7 | ```java 8 | final String s = Result.suppress(() -> readFile("myFile.txt")) 9 | .expect("That didn't work!"); 10 | ``` 11 | 12 | Or, be extremely specific: 13 | 14 | ```java 15 | final String s = Result 16 | .define(FileNotFoundException.class, e -> log.warn("Missing file: {}", e)) 17 | .define(IllegalArgumentException.class, Result::THROW) 18 | .define(IOException.class, Result::WARN) 19 | .suppressNullable(() -> readFile("myFile.txt")) 20 | .ifEmpty(() -> log.warn("File was empty!")) 21 | .ifErr(e -> log.warn("Couldn't read file!")) 22 | .ifOk(t -> log.info("Good job!")) 23 | .defaultIfEmpty(() -> "Didn't want a book") 24 | .resolve(e -> "Couldn't read the book") 25 | .expose(); 26 | ``` 27 | 28 | This interface is capable of completely replacing the standard try-catch and 29 | try-with-resources notations in Java. It has two essential modes of use: 30 | 31 | 1. A better return type for functions that use standard error handling procedures 32 | 2. A wrapper around functions that do not. 33 | 34 | ## Installing 35 | 36 | FResult is available on Maven Central! To use it in your project, add the following 37 | dependency in your build.gradle or pom.xml: 38 | 39 | ```groovy 40 | implementation group: 'com.personthecat', name: 'fresult', version: '3.0' 41 | ``` 42 | 43 | ## A Better Return Type 44 | 45 | Let's take a look at some examples of methods that use this new return type and 46 | then examine how they would be applied. 47 | 48 | ```java 49 | // Return the product of each block. 50 | public static Result betterReturn() { 51 | final File f = getFile(); 52 | try { 53 | return Result.ok(getContents(f)); 54 | } catch (final IOException e) { 55 | return Result.err(e); 56 | } 57 | } 58 | 59 | // Create and return a new error directly. 60 | public static Result betterReturnAlt() { 61 | final File f = getFile(); 62 | return testConditions(f) 63 | ? Result.ok(getContents(f)) 64 | : Result.err(new IOException()); 65 | } 66 | ``` 67 | 68 | ### Implementation 69 | 70 | Each of these methods returns a **complete result**. This means that any error 71 | present inside of the wrapper is effectively **reifiable**. In other words, it 72 | contains a knowable type and thus can be safely interacted with. 73 | 74 | FResult provides a full suite of functional utilities for interacting with the 75 | underlying values and errors in this type. 76 | 77 | Here, you can see a few of those methods in action: 78 | 79 | ```java 80 | // Handle all outcome scenarios. 81 | final Result r1 = betterReturn() 82 | .ifErr(Result::WARN) // Output a warning message 83 | .ifOk(ContentConsumer::apply); // Consume the text output 84 | 85 | // Transform the data into a common type. 86 | final int numLines = r1 87 | .fold(t -> t.lines().size(), e -> 0); 88 | 89 | // Immediately supply an alternate value. 90 | final String output = betterReturnAlt() 91 | .orElseGet(String::new); 92 | 93 | // Map the underlying data to a new type. 94 | final int hashCode = betterReturnAlt() 95 | .map(Object::hashCode) 96 | .orElse(0); 97 | ``` 98 | 99 | ## A Try-Catch Replacement Wrapper 100 | 101 | Now let's look at the second use case in which we're wrapping a standard, 102 | throwing method. 103 | 104 | ```java 105 | // Standard conventions to be wrapped. 106 | public static String toWrap() throws IOException { 107 | final File f = getFile(); 108 | return getContents(f); 109 | } 110 | ``` 111 | 112 | ### Implementation 113 | 114 | FResult provides a series of factory methods for wrapping standard error-prone 115 | conventions, including `#of`, `#suppress`, `#nullable`, `#with`, `#define`, 116 | `#resolve`, and more. 117 | 118 | Let's start with the first (and most important) option: `Result#of`. The first 119 | thing you'll notice is that the return type is **not assignable to Result**. 120 | 121 | ```java 122 | // Generate instructions for wrapping this method. 123 | final PartialResult r1 = Result.of(Name::toWrap); 124 | 125 | // Consume these instructions and get a Result. 126 | final Result r2 = r1.ifErr(e -> log.warn("Oops!")); 127 | ``` 128 | 129 | The output of `Result#of` is a type of `Result$Pending`, which implements 130 | `PartialResult`. This name has two very important implications: 131 | 132 | 1. The result is lazily-evaluated and **does not exist yet**. 133 | 2. The wrapper **does not provide a complete set of utilities**. In other words, 134 | it is an **incomplete result**. 135 | 136 | The reason for this type of design is the product of **type erasure**, which will 137 | be discussed at the [end of this article](#type-erasure). In short, when a method 138 | returns a `PartialResult`, **you must acknowledge the error**. 139 | 140 | ## Ignoring Specific Error Types 141 | 142 | Alternatively, if you would like to simply ignore the specific type of error 143 | being returned, you can employ `Pending#isAnyErr`, or `Pending#expectAnyErr`. 144 | 145 | Also see `Result#suppress` for returning a complete `Result` which 146 | may contain **any kind of error**. Here's how that would look: 147 | 148 | ```java 149 | // No need to acknowledge the error, as it can be any type. 150 | final Result r1 = Result.suppress(Name::toWrap); 151 | 152 | // Optionally discard the error and directly expose the contents. 153 | final String r2 = Result.suppress(Name::toWrap) 154 | .resolve(e -> "") // Type must now be a Result$Value 155 | .expose(); // The data can safely be exposed 156 | ``` 157 | 158 | ## Handling `null` Return Values 159 | 160 | FResult is also capable of wrapping methods that may return null. This functionality 161 | if provided via `OptionalResult` and `PartialOptionalResult`. Below are a few of the 162 | factory methods provided for handling nullable types. 163 | 164 | * `Result#nullable(T)` -> `OptionalResult` 165 | * `Result#nullable(ThrowingSupplier)` -> `PartialOptionalResult` 166 | * `Result#nullable(Optional)` -> `OptionalResult` 167 | * `Result#nullable(ThrowingOptionalSupplier)` -> `PartialOptionalResult` 168 | 169 | 170 | Let's see what it looks like to use these wrappers: 171 | 172 | ```java 173 | // Known types (OptionalResult) 174 | final Object r1 = Result.nullable(potentiallyNullValue) 175 | .orElseGet(Object::new); // Alternate value if null 176 | 177 | // Unknown types (PartialOptionalResult) 178 | final Object r2 = Result.nullable(Name::mayReturnNullOrFail) 179 | .ifErr(e -> log.warn("Error on output: {}", e)) 180 | .orElseGet(Object::new); 181 | ``` 182 | 183 | Methods that wish to return nullable Result types must return an instance of 184 | `OptionalResult`. Here's how that would look: 185 | 186 | ```java 187 | // Output may still be null even if no exception is thrown. 188 | public OptionalResult getBook() { 189 | try { 190 | return Result.nullable(this.dao.getBook()); 191 | } catch (final SQLSyntaxException e) { 192 | return Result.err(e); 193 | } 194 | } 195 | ``` 196 | 197 | ## Wrapping Try-With-Resources 198 | 199 | FResult also provides a couple of companion utilities, `WithResource` and 200 | `WithResources`, which provide support for `AutoCloseable` resources. 201 | 202 | Here's how you can use these methods: 203 | 204 | ```java 205 | // Use a single resource via method chainging 206 | final String book = Result 207 | .with(() -> new FileReader("book.txt")) // Get the resource 208 | .suppress(reader -> { /* read file */ }) // Use the resource 209 | .orElseGet(e -> ""); // Handle errors 210 | 211 | // Use a single resource in one method 212 | final String book = Result.with(() -> new FileReader("book.txt"), reader -> { 213 | // read file 214 | }).orElseGet(e -> ""); 215 | 216 | // Use multiple resources 217 | final String book = Result 218 | .with(() -> new FileReader("book.txt")) 219 | .with(SecondResource::new) // Either a supplier or a function 220 | .suppress((reader, second) -> { /* read file */ }) 221 | .orElseGet(e -> ""); 222 | ``` 223 | 224 | ## Handling Multiple Error Types 225 | 226 | FResult provides a couple of companion utilities designed for handled multiple 227 | different types of exceptions individually. Let's explore these utilities and see 228 | how they can enable you to be very specific in handling errors. 229 | 230 | ### Protocol 231 | 232 | We'll start by **defining** a `Protocol`, which contains a set of procedures for 233 | handling different errors. 234 | 235 | ```java 236 | final Protocol p = Result 237 | .define(FileNotFoundException.class, Result::THROW) // Crash if not found 238 | .define(SecurityException.class, Result::THROW) // Or if the program lacks permission. 239 | .define(IOException.class, Result::WARN); // All other IO issues can be ignored. 240 | ``` 241 | 242 | This object can be used to spawn a new `Result` or can be stored somewhere and be 243 | passed into multiple `Result#orElseTry` handlers and be reused. 244 | 245 | ```java 246 | final Result r = Result 247 | .define(FileNotFound.class, e -> log.warn("Error founnd: {}", e)) 248 | .define(RuntimeException.class, Result::THROW) // Exit thread. 249 | .suppress(() -> readFile("myFile.txt")); 250 | ``` 251 | 252 | ### Resolver 253 | 254 | FResult also provides a `Resolver`, which contains a set of procedures for 255 | **resolving** different exceptions to explicit values. Its use is very similar to that 256 | of `Protocol`. 257 | 258 | ```java 259 | final String status s = Result 260 | .resolve(FileNotFound.class, e -> "Couldn't find it!") 261 | .resolve(RuntimeException.class, e -> "Something else went wrong!") 262 | .suppress(() -> readFile("myFile.txt")) // A definite value is provided 263 | .expose(); // Because this is Result$value, it may be exposed safely. 264 | ``` 265 | 266 | ## Using Result Imperatively 267 | 268 | FResult provides a complete set of concrete implementations representing different 269 | outcomes. If you prefer to use this type imperatively, you may check its type and 270 | cast it, allowing the underlying value or error to be exposed. 271 | 272 | ```java 273 | final Result r = getResult(); 274 | if (r.isOk()) { 275 | final String v = ((Value) r).expose(); 276 | } 277 | ``` 278 | 279 | Note that casting `PartialResult` types is especially unsafe, as the result may not 280 | have been computed yet, and thus you should only rely on the output of `#ifErr`. 281 | 282 | ```java 283 | final Result r = getPartialResult() 284 | .ifErr(e -> log.warn("Error found: {}", e)); 285 | if (r.isErr()) { 286 | final IOException e = ((Error) r).expose(); 287 | } 288 | ``` 289 | 290 | A good rule of thumb: if you can't assign it to `Result` or `OptionalResult`, don't 291 | use it. 292 | 293 | ## Other Factory Methods 294 | 295 | Finally, FResult also provides a couple of static factory methods for interacting 296 | with multiple known and partial results. 297 | 298 | ```java 299 | // Get a single Result 300 | final Result r1 = getFirstResult(); 301 | final Result r2 = getSecondResult(); 302 | final Result join = Result.join(r1, r2); 303 | 304 | // Get a single Result, E> 305 | final Result r3 = getThirdResult(); 306 | final Result r4 = getFourthResult(); 307 | final Result, Throwable> list = Result.collect(r3, r4); 308 | ``` 309 | 310 | #### Type Erasure 311 | 312 | If you're unfamiliar, **type erasure** is the process by which generified types 313 | lose their generic parameters at runtime in Java, depending on the context in 314 | which they're used. 315 | 316 | For this reason, it is **impossible** to catch an exception based on a generic 317 | type, as the exact type cannot be known at runtime. To work around this, FResult 318 | exploits Java's generic type coercion mechanics to achieve a type-safe guarantee. 319 | 320 | Let's take a closer look: 321 | 322 | ```java 323 | // Output cannot contain a different exception. 324 | final Result r1 = Result.of(Name::toWrap) 325 | .ifErr(e -> { /* handle error */ }); // Type is implicitly cast 326 | 327 | // Acknowledge and immediately discard the exception. 328 | final Optional r2 = Result.of(Name::toWrap) 329 | .get(e -> { /* handle error */ }); // Also resolves the type 330 | ``` 331 | 332 | You should notice two things from this example: 333 | 334 | 1. The use of `e` implicitly casts the underlying error to its 335 | expected type. 336 | 2. The output is now assignable to a standard `Result`. 337 | 338 | FResult uses this mechanism to guarantee that an unexpected type of error can 339 | never be caught by the wrapper. If one is, it will be rethrown as a 340 | `WrongErrorException`. This is only possible because `e` is returned to the 341 | call site while still being within the scope of the wrapper. 342 | 343 | If we apply this knowledge, we can see that it becomes safe to use the output 344 | of `Result#of` as a standard `Result` **after applying** `ifErr`. 345 | 346 | The following methods are considered **safe** and are ideal candidates for the 347 | majority of use cases immediately after calling `Result#of`: 348 | 349 | * `Pending#ifErr(Consumer)` 350 | * `Pending#get(Consumer)` 351 | * `Pending#fold(Function, Function)` 352 | * `Pending#orElseGet(Function)` 353 | * `Pending#orElseTry(ThrowingFunction)` 354 | * `Pending#expect` and `Pending#expectF` 355 | 356 | Note that if you would like to continue using the wrapper as a type of 357 | `Result`, **you must always call** `ifErr`. 358 | 359 | ## Motivations and Cons 360 | 361 | Wrapping error-prone procedures in a functional interface is a lot more expressive and 362 | can be much easier to maintain. It gives callers a more convenient interface for 363 | supplying alternate values and reporting errors to the end-user. 364 | 365 | However, this comes at a cost. 366 | 367 | It is certainly less safe and less specific than vanilla error handling in Java, 368 | as it sometimes requires a bit of runtime reflection and only accepts one error 369 | parameter by default. It also requires a bit of memory overhead which likely has 370 | at least a minor impact on performance. For some, functional error handling may be 371 | a lot more readable than imperative error handling, but this may not apply for most 372 | Java developers, as it strays from the standard conventions they're used to. 373 | 374 | As a result, this project is ideal for frameworks and new projects seeking to explore 375 | novel design strategies. 376 | -------------------------------------------------------------------------------- /src/main/java/personthecat/fresult/OptionalResult.java: -------------------------------------------------------------------------------- 1 | package personthecat.fresult; 2 | 3 | import org.jetbrains.annotations.CheckReturnValue; 4 | import personthecat.fresult.exception.ResultUnwrapException; 5 | import personthecat.fresult.exception.WrongErrorException; 6 | import personthecat.fresult.functions.OptionalResultFunction; 7 | import personthecat.fresult.functions.ThrowingFunction; 8 | import personthecat.fresult.functions.ThrowingRunnable; 9 | import personthecat.fresult.functions.ThrowingSupplier; 10 | 11 | import java.io.Serializable; 12 | import java.util.Optional; 13 | import java.util.function.Consumer; 14 | import java.util.function.Function; 15 | import java.util.function.Predicate; 16 | import java.util.function.Supplier; 17 | 18 | import static personthecat.fresult.Shorthand.f; 19 | 20 | @SuppressWarnings({"unused", "UnusedReturnValue"}) 21 | public sealed interface OptionalResult 22 | extends BasicResult, Serializable 23 | permits Result.Empty, Result.Error, Result.Value, Result { 24 | 25 | /** 26 | * Consumes instructions for what to do if a given result contains no value. This 27 | * method eliminates any uncertainty surrounding whether null values can exist within 28 | * the wrapper. As a result, its type will be upgraded to a more permissive 29 | * {@link Result}. 30 | * 31 | * @param defaultGetter Supplies a default value if the wrapper does not contain one. 32 | * @return An upgraded {@link Result} for handling known error types. 33 | */ 34 | Result defaultIfEmpty(final Supplier defaultGetter); 35 | 36 | /** 37 | * Generates an error in the case where no value is present at all in this wrapper. 38 | * 39 | *

As with {@link #defaultIfEmpty(Supplier)}, this method eliminates the possibility 40 | * of empty state, thus allowing it to return a standard {@link Result}. 41 | * 42 | * @param defaultGetter Supplies an error if the wrapper contains no value. 43 | * @return An upgraded {@link Result} for handling known error types. 44 | */ 45 | Result errIfEmpty(final Supplier defaultGetter); 46 | 47 | /** 48 | * Returns whether no value was returned in the process. 49 | * 50 | * e.g. 51 | *

 52 |      *   final OptionalResult<Object, RuntimeException> result = getOptionalResult();
 53 |      *   // Compute the result and proceed only if it returns null.
 54 |      *   if (result.isEmpty()) {
 55 |      *       ...
 56 |      *   }
 57 |      * 
58 | * 59 | * @return true, if no value is present. 60 | */ 61 | @CheckReturnValue 62 | boolean isEmpty(); 63 | 64 | /** 65 | * Consumes a procedure to be executed if no value is present in the wrapper. 66 | * 67 | * @param f A function to run if this wrapper contains no value. 68 | * @return This, or else an upgraded {@link PartialResult}. 69 | */ 70 | OptionalResult ifEmpty(final Runnable f); 71 | 72 | /** 73 | * Returns whether the expected type of error occurred in the process. 74 | * 75 | * e.g. 76 | *
 77 |      *   final Result<Void, RuntimeException> result = getResult();
 78 |      *   // Compute the result and proceed only if it errs.
 79 |      *   if (result.isErr()) {
 80 |      *       ...
 81 |      *   }
 82 |      * 
83 | * 84 | * @return true, if an error is present. 85 | */ 86 | @CheckReturnValue 87 | boolean isErr(); 88 | 89 | /** 90 | * Accepts an expression for what to do in the event of an error being present. 91 | *

92 | * Use this whenever you want to functionally handle both code paths (i.e. error 93 | * vs. value). 94 | *

95 | * @throws WrongErrorException If the underlying error is an unexpected type. 96 | * @param f A function consuming the error, if present. 97 | * @return This, or else a complete {@link Result}. 98 | */ 99 | OptionalResult ifErr(final Consumer f); 100 | 101 | /** 102 | * Returns whether a value was yielded or no error occurred. 103 | * 104 | *

e.g.

105 | *
106 |      *   final Result<Void, RuntimeException> result = getResult();
107 |      *   // Compute the result and proceed only if it does not err.
108 |      *   if (result.isOk()) {
109 |      *       ...
110 |      *   }
111 |      * 
112 | * 113 | * @return true, if a value is present. 114 | */ 115 | @CheckReturnValue 116 | boolean isOk(); 117 | 118 | /** 119 | * Accepts an expression for what to do in the event of no error 120 | * being present. Use this whenever you want to functionally handle 121 | * both code paths (i.e. error vs. value). 122 | * 123 | * @param f A function consuming the value, if present. 124 | * @return This, or else a complete {@link Result}. 125 | */ 126 | OptionalResult ifOk(Consumer f); 127 | 128 | /** 129 | * Effectively casts this object into a standard Optional instance. 130 | * Prefer calling {@link OptionalResult#get(Consumer)}, as this removes the need for 131 | * any implicit error checking. 132 | * 133 | * @return The underlying value, wrapped in {@link Optional}. 134 | */ 135 | @CheckReturnValue 136 | Optional get(); 137 | 138 | /** 139 | * Retrieves the underlying error, wrapped in Optional. 140 | * 141 | * @return The underlying error, wrapped in {@link Optional}. 142 | */ 143 | @CheckReturnValue 144 | Optional getErr(); 145 | 146 | /** 147 | * Yields the underlying value or else the input. This is equivalent to 148 | * running `getResult().get(e -> {...}).orElse();` 149 | * 150 | * @return The underlying value, or else the input. 151 | */ 152 | @CheckReturnValue 153 | T orElse(T val); 154 | 155 | /** 156 | * Variant of {@link PartialResult#orElseGet(Function)} which does not specifically 157 | * consume the error. 158 | * 159 | * @param f A function which supplies a new value if none is present in the 160 | * wrapper. 161 | * @return The underlying value, or else func.get(). 162 | */ 163 | @CheckReturnValue 164 | T orElseGet(final Supplier f); 165 | 166 | /** 167 | * Variant of {@link Result#orElseTry(ThrowingFunction)} which ignores any errors present 168 | * in the wrapper. 169 | * 170 | * @param f A new function to attempt in the presence of an error. 171 | * @return The pending result of the new function, if an error is present, 172 | * or else a complete {@link Result}. 173 | */ 174 | @CheckReturnValue 175 | PartialResult orElseTry(final ThrowingSupplier f); 176 | 177 | /** 178 | * Variant of {@link Result#orElseTry(ThrowingFunction)} which handles the error when 179 | * given a specific {@link Protocol}. 180 | * 181 | * @param protocol Instructions for handling errors beyond this point. 182 | * @param f A new function to attempt in the presence of an error. 183 | * @return A result which may contain either a value or any error. 184 | */ 185 | @CheckReturnValue 186 | Result orElseTry(final Protocol protocol, final ThrowingSupplier f); 187 | 188 | /** 189 | * Replaces the underlying value with the result of func.apply(). Use this 190 | * whenever you need to map a potential value to a different value. 191 | * 192 | *

e.g.

193 | *
194 |      *   final int secretArrayLength = Result.of(() -> getSecretArray())
195 |      *      .get(e -> {...}) // Handle any errors.
196 |      *      .map(array -> array.length) // Return the length instead.
197 |      *      .orElse(0); // Didn't work. Call Optional#orElse().
198 |      * 
199 | * 200 | * @param f The mapper applied to the value. 201 | * @return A new Result with its value mapped. 202 | */ 203 | @CheckReturnValue 204 | OptionalResult map(final Function f); 205 | 206 | /** 207 | * Replaces the underlying error with the result of func.apply(). Use this 208 | * whenever you need to map a potential error to another error. 209 | * 210 | * @param f The mapper applied to the error. 211 | * @return A new Result with its error mapped. 212 | */ 213 | @CheckReturnValue 214 | OptionalResult mapErr(final Function f); 215 | 216 | /** 217 | * Replaces the entire value with a new result, if present. Use this whenever 218 | * you need to map a potential value to another function which yields a Result. 219 | * 220 | * @param f A function which yields a new Result wrapper if a value is present. 221 | * @return The new function yielded, if a value is present, else this. 222 | */ 223 | @CheckReturnValue 224 | OptionalResult flatMap(final OptionalResultFunction f); 225 | 226 | /** 227 | * Replaces the entire value with a new result, if present. Use this whenever 228 | * you need to map a potential error to another function which yields a Result. 229 | * 230 | * @param f A function which yields a new Result wrapper if an error is present. 231 | * @return The new function yielded, if an error is present, or else a complete 232 | * {@link Result}. 233 | */ 234 | @CheckReturnValue 235 | OptionalResult flatMapErr(final OptionalResultFunction f); 236 | 237 | /** 238 | * Filters the underlying value out of the wrapper based on the given condition. 239 | * 240 | *

e.g.

241 | *
242 |      *   Result.of(() -> "Hello, world!") // Value is present
243 |      *     .filter(t -> false) // Value is removed.
244 |      *     .assertEmpty(); // No exception is thrown.
245 |      * 
246 | * 247 | * @param f A predicate which determines whether to keep the underlying value, if present. 248 | * @return A new result with no value, or else this. 249 | */ 250 | @CheckReturnValue 251 | OptionalResult filter(final Predicate f); 252 | 253 | /** 254 | * Variant of {@link #filter(Predicate)} which converts the filtered value into an error. 255 | * 256 | *

e.g.

257 | *
258 |      *   Result.of(() -> "Hello, world!") // Value is present
259 |      *     .filter(String::isEmpty, Throwable::new)
260 |      *     .unwrapErr(); // No exception is thrown.
261 |      * 
262 | * 263 | * @param f A predicate which determines whether to keep the underlying value, if present. 264 | * @param err Supplies a default error if the predicate is matched. 265 | * @return A new result with an error, or else this. 266 | */ 267 | @CheckReturnValue 268 | OptionalResult filter(final Predicate f, final Supplier err); 269 | 270 | /** 271 | * Filters the underlying error out of the wrapper based on the given condition. 272 | * 273 | *

e.g.

274 | *
275 |      *   Result.of(() -> throwException()) // Error is present
276 |      *     .filterErr(e -> false) // Error is removed.
277 |      *     .assertEmpty(); // No exception is thrown.
278 |      * 
279 | * 280 | * @param f A predicate which determines whether to keep the underlying error, if present. 281 | * @return A new result with no error, or else this. 282 | */ 283 | @CheckReturnValue 284 | OptionalResult filterErr(final Predicate f); 285 | 286 | /** 287 | * Variant of {@link #filterErr(Predicate)} which converts the filtered error into a value. 288 | * 289 | *

e.g.

290 | *
291 |      *   Result.<Integer, Exception>of(() -> throwException()) // Error is present
292 |      *     .filterErr(e -> false, () -> 0) // Error is removed.
293 |      *     .unwrap(); // No exception is thrown.
294 |      * 
295 | * 296 | * @param f A predicate which determines whether to keep the underlying error, if present. 297 | * @param val Supplies a default value if the predicate is matched. 298 | * @return A new result with a value, or else this. 299 | */ 300 | @CheckReturnValue 301 | OptionalResult filterErr(final Predicate f, final Supplier val); 302 | 303 | /** 304 | * Consumes an event to run in the event of success in this wrapper. 305 | * 306 | *

e.g.

307 | *
308 |      *   Result.any(() -> "Hello, world!") // Result is OK
309 |      *     .andThen(String::length); // becomes a Result<Integer, E>
310 |      * 
311 | * 312 | * @param f The event to run, if OK. 313 | * @param The new type of value being consumed by the wrapper. 314 | * @return A new result containing the output of this function, or else this. 315 | */ 316 | @CheckReturnValue 317 | OptionalResult andThen(final Function f); 318 | 319 | /** 320 | * Variant of {@link #andThen(Function)} which may fail. Any error returned by this 321 | * event must be acknowledged. 322 | * 323 | *

e.g.

324 | *
325 |      *   Result.any(() -> "Hello, world!) // Result is OK
326 |      *     .andThenTry(s -> throwException()) // Result is not OK
327 |      *     .ifErr(Result::WARN); // This problem will be logged.
328 |      * 
329 | * 330 | * @param attempt The next procedure to attempt, if OK. 331 | * @param The new type of value being consumed by the wrapper. 332 | * @return A new result containing the output of this function, or else this. 333 | */ 334 | @CheckReturnValue 335 | PartialOptionalResult andThenTry(final ThrowingFunction attempt); 336 | 337 | /** 338 | * Variant of {@link #andThenTry} which is allowed to throw any exception. 339 | * Returns a less restrictive wrapper. 340 | * 341 | * @param attempt The next procedure to attempt, if OK. 342 | * @param The new type of value being consumed by the wrapper. 343 | * @return A new result containing the output of this function, or else this. 344 | */ 345 | @CheckReturnValue 346 | OptionalResult andThenSuppress(final ThrowingFunction attempt); 347 | 348 | /** 349 | * Variant of {@link #andThen(Function)} which ignores the value in the wrapper. 350 | * 351 | * @param f The event to run, if OK. 352 | * @return A new result containing the output of this function, or else this. 353 | */ 354 | OptionalResult andThen(final Runnable f); 355 | 356 | /** 357 | * Variant of {@link #andThenTry(ThrowingFunction)} which ignores the value in 358 | * the wrapper. 359 | * 360 | * @param attempt The next procedure to attempt, if OK. 361 | * @return A new result containing the output of this function, or else this. 362 | */ 363 | @CheckReturnValue 364 | PartialOptionalResult andThenTry(final ThrowingRunnable attempt); 365 | 366 | /** 367 | * Variant of {@link #andThenSuppress(ThrowingFunction)} which ignores the value 368 | * in the wrapper. 369 | * 370 | * @param attempt The next procedure to attempt, if OK. 371 | * @return A new result containing the output of this function, or else this. 372 | */ 373 | @CheckReturnValue 374 | OptionalResult andThenSuppress(final ThrowingRunnable attempt); 375 | 376 | /** 377 | * Attempts to retrieve the underlying error, asserting that one must exist. 378 | * 379 | * @throws ResultUnwrapException If no error is present to be unwrapped. 380 | * @return The underlying error. 381 | */ 382 | @CheckReturnValue 383 | default E unwrapErr() { 384 | return expectErr("Attempted to unwrap a result with no error."); 385 | } 386 | 387 | /** 388 | * Asserts that no value or error will be present within the wrapper. 389 | * 390 | * @throws ResultUnwrapException If a value or an error is present. 391 | */ 392 | default void assertEmpty() { 393 | this.expectEmpty("Wrapper contains a value or error."); 394 | } 395 | 396 | /** 397 | * Returns a generic type of {@link Throwable} which could be any exception. 398 | * 399 | *

400 | * You may wish to use this method instead of {@link Result#expectErr} as it is 401 | * guaranteed to have a valid output and will never result in a {@link ClassCastException}. 402 | *

403 | * 404 | * @throws ResultUnwrapException If no error is present to be unwrapped. 405 | * @param message The message to display in the event of an error. 406 | * @return The underlying error. 407 | */ 408 | E expectErr(final String message); 409 | 410 | /** 411 | * @see PartialOptionalResult#expectErr(String) 412 | * @throws ResultUnwrapException If no error is present to be unwrapped. 413 | * @param message The message to display in the event of an error. 414 | * @param args A series of interpolated arguments (replacing {}). 415 | * @return The underlying error. 416 | */ 417 | default E expectErr(final String message, final Object... args) { 418 | return this.expectErr(f(message, args)); 419 | } 420 | 421 | /** 422 | * Asserts that no value or error will be present within the wrapper. 423 | * 424 | * @param message The message to display in the event of an error. 425 | * @throws ResultUnwrapException If a value or an error is present. 426 | */ 427 | void expectEmpty(final String message); 428 | 429 | /** 430 | * Formatted variant of {@link #expectEmpty}. 431 | * 432 | * @param message The message to display in the event of an error. 433 | * @param args A series of interpolated arguments (replacing {}). 434 | * @throws ResultUnwrapException If a value or an error is present. 435 | */ 436 | default void expectEmpty(final String message, final Object... args) { 437 | this.expectEmpty(f(message, args)); 438 | } 439 | 440 | /** 441 | * Variant of {@link #unwrap} which throws the original error, if applicable. 442 | * 443 | * @throws E The original error, if present. 444 | * @return The underlying value. 445 | */ 446 | default T orElseThrow() throws E { 447 | this.throwIfErr(); 448 | return this.unwrap(); 449 | } 450 | 451 | /** 452 | * If an error is present, it will be thrown by this method. 453 | * 454 | * @throws E The original error, if present. 455 | */ 456 | void throwIfErr() throws E; 457 | } 458 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU LESSER GENERAL PUBLIC LICENSE 2 | Version 2.1, February 1999 3 | 4 | Copyright (C) 1991, 1999 Free Software Foundation, Inc. 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | [This is the first released version of the Lesser GPL. It also counts 10 | as the successor of the GNU Library Public License, version 2, hence 11 | the version number 2.1.] 12 | 13 | Preamble 14 | 15 | The licenses for most software are designed to take away your 16 | freedom to share and change it. By contrast, the GNU General Public 17 | Licenses are intended to guarantee your freedom to share and change 18 | free software--to make sure the software is free for all its users. 19 | 20 | This license, the Lesser General Public License, applies to some 21 | specially designated software packages--typically libraries--of the 22 | Free Software Foundation and other authors who decide to use it. You 23 | can use it too, but we suggest you first think carefully about whether 24 | this license or the ordinary General Public License is the better 25 | strategy to use in any particular case, based on the explanations below. 26 | 27 | When we speak of free software, we are referring to freedom of use, 28 | not price. Our General Public Licenses are designed to make sure that 29 | you have the freedom to distribute copies of free software (and charge 30 | for this service if you wish); that you receive source code or can get 31 | it if you want it; that you can change the software and use pieces of 32 | it in new free programs; and that you are informed that you can do 33 | these things. 34 | 35 | To protect your rights, we need to make restrictions that forbid 36 | distributors to deny you these rights or to ask you to surrender these 37 | rights. These restrictions translate to certain responsibilities for 38 | you if you distribute copies of the library or if you modify it. 39 | 40 | For example, if you distribute copies of the library, whether gratis 41 | or for a fee, you must give the recipients all the rights that we gave 42 | you. You must make sure that they, too, receive or can get the source 43 | code. If you link other code with the library, you must provide 44 | complete object files to the recipients, so that they can relink them 45 | with the library after making changes to the library and recompiling 46 | it. And you must show them these terms so they know their rights. 47 | 48 | We protect your rights with a two-step method: (1) we copyright the 49 | library, and (2) we offer you this license, which gives you legal 50 | permission to copy, distribute and/or modify the library. 51 | 52 | To protect each distributor, we want to make it very clear that 53 | there is no warranty for the free library. Also, if the library is 54 | modified by someone else and passed on, the recipients should know 55 | that what they have is not the original version, so that the original 56 | author's reputation will not be affected by problems that might be 57 | introduced by others. 58 | 59 | Finally, software patents pose a constant threat to the existence of 60 | any free program. We wish to make sure that a company cannot 61 | effectively restrict the users of a free program by obtaining a 62 | restrictive license from a patent holder. Therefore, we insist that 63 | any patent license obtained for a version of the library must be 64 | consistent with the full freedom of use specified in this license. 65 | 66 | Most GNU software, including some libraries, is covered by the 67 | ordinary GNU General Public License. This license, the GNU Lesser 68 | General Public License, applies to certain designated libraries, and 69 | is quite different from the ordinary General Public License. We use 70 | this license for certain libraries in order to permit linking those 71 | libraries into non-free programs. 72 | 73 | When a program is linked with a library, whether statically or using 74 | a shared library, the combination of the two is legally speaking a 75 | combined work, a derivative of the original library. The ordinary 76 | General Public License therefore permits such linking only if the 77 | entire combination fits its criteria of freedom. The Lesser General 78 | Public License permits more lax criteria for linking other code with 79 | the library. 80 | 81 | We call this license the "Lesser" General Public License because it 82 | does Less to protect the user's freedom than the ordinary General 83 | Public License. It also provides other free software developers Less 84 | of an advantage over competing non-free programs. These disadvantages 85 | are the reason we use the ordinary General Public License for many 86 | libraries. However, the Lesser license provides advantages in certain 87 | special circumstances. 88 | 89 | For example, on rare occasions, there may be a special need to 90 | encourage the widest possible use of a certain library, so that it becomes 91 | a de-facto standard. To achieve this, non-free programs must be 92 | allowed to use the library. A more frequent case is that a free 93 | library does the same job as widely used non-free libraries. In this 94 | case, there is little to gain by limiting the free library to free 95 | software only, so we use the Lesser General Public License. 96 | 97 | In other cases, permission to use a particular library in non-free 98 | programs enables a greater number of people to use a large body of 99 | free software. For example, permission to use the GNU C Library in 100 | non-free programs enables many more people to use the whole GNU 101 | operating system, as well as its variant, the GNU/Linux operating 102 | system. 103 | 104 | Although the Lesser General Public License is Less protective of the 105 | users' freedom, it does ensure that the user of a program that is 106 | linked with the Library has the freedom and the wherewithal to run 107 | that program using a modified version of the Library. 108 | 109 | The precise terms and conditions for copying, distribution and 110 | modification follow. Pay close attention to the difference between a 111 | "work based on the library" and a "work that uses the library". The 112 | former contains code derived from the library, whereas the latter must 113 | be combined with the library in order to run. 114 | 115 | GNU LESSER GENERAL PUBLIC LICENSE 116 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 117 | 118 | 0. This License Agreement applies to any software library or other 119 | program which contains a notice placed by the copyright holder or 120 | other authorized party saying it may be distributed under the terms of 121 | this Lesser General Public License (also called "this License"). 122 | Each licensee is addressed as "you". 123 | 124 | A "library" means a collection of software functions and/or data 125 | prepared so as to be conveniently linked with application programs 126 | (which use some of those functions and data) to form executables. 127 | 128 | The "Library", below, refers to any such software library or work 129 | which has been distributed under these terms. A "work based on the 130 | Library" means either the Library or any derivative work under 131 | copyright law: that is to say, a work containing the Library or a 132 | portion of it, either verbatim or with modifications and/or translated 133 | straightforwardly into another language. (Hereinafter, translation is 134 | included without limitation in the term "modification".) 135 | 136 | "Source code" for a work means the preferred form of the work for 137 | making modifications to it. For a library, complete source code means 138 | all the source code for all modules it contains, plus any associated 139 | interface definition files, plus the scripts used to control compilation 140 | and installation of the library. 141 | 142 | Activities other than copying, distribution and modification are not 143 | covered by this License; they are outside its scope. The act of 144 | running a program using the Library is not restricted, and output from 145 | such a program is covered only if its contents constitute a work based 146 | on the Library (independent of the use of the Library in a tool for 147 | writing it). Whether that is true depends on what the Library does 148 | and what the program that uses the Library does. 149 | 150 | 1. You may copy and distribute verbatim copies of the Library's 151 | complete source code as you receive it, in any medium, provided that 152 | you conspicuously and appropriately publish on each copy an 153 | appropriate copyright notice and disclaimer of warranty; keep intact 154 | all the notices that refer to this License and to the absence of any 155 | warranty; and distribute a copy of this License along with the 156 | Library. 157 | 158 | You may charge a fee for the physical act of transferring a copy, 159 | and you may at your option offer warranty protection in exchange for a 160 | fee. 161 | 162 | 2. You may modify your copy or copies of the Library or any portion 163 | of it, thus forming a work based on the Library, and copy and 164 | distribute such modifications or work under the terms of Section 1 165 | above, provided that you also meet all of these conditions: 166 | 167 | a) The modified work must itself be a software library. 168 | 169 | b) You must cause the files modified to carry prominent notices 170 | stating that you changed the files and the date of any change. 171 | 172 | c) You must cause the whole of the work to be licensed at no 173 | charge to all third parties under the terms of this License. 174 | 175 | d) If a facility in the modified Library refers to a function or a 176 | table of data to be supplied by an application program that uses 177 | the facility, other than as an argument passed when the facility 178 | is invoked, then you must make a good faith effort to ensure that, 179 | in the event an application does not supply such function or 180 | table, the facility still operates, and performs whatever part of 181 | its purpose remains meaningful. 182 | 183 | (For example, a function in a library to compute square roots has 184 | a purpose that is entirely well-defined independent of the 185 | application. Therefore, Subsection 2d requires that any 186 | application-supplied function or table used by this function must 187 | be optional: if the application does not supply it, the square 188 | root function must still compute square roots.) 189 | 190 | These requirements apply to the modified work as a whole. If 191 | identifiable sections of that work are not derived from the Library, 192 | and can be reasonably considered independent and separate works in 193 | themselves, then this License, and its terms, do not apply to those 194 | sections when you distribute them as separate works. But when you 195 | distribute the same sections as part of a whole which is a work based 196 | on the Library, the distribution of the whole must be on the terms of 197 | this License, whose permissions for other licensees extend to the 198 | entire whole, and thus to each and every part regardless of who wrote 199 | it. 200 | 201 | Thus, it is not the intent of this section to claim rights or contest 202 | your rights to work written entirely by you; rather, the intent is to 203 | exercise the right to control the distribution of derivative or 204 | collective works based on the Library. 205 | 206 | In addition, mere aggregation of another work not based on the Library 207 | with the Library (or with a work based on the Library) on a volume of 208 | a storage or distribution medium does not bring the other work under 209 | the scope of this License. 210 | 211 | 3. You may opt to apply the terms of the ordinary GNU General Public 212 | License instead of this License to a given copy of the Library. To do 213 | this, you must alter all the notices that refer to this License, so 214 | that they refer to the ordinary GNU General Public License, version 2, 215 | instead of to this License. (If a newer version than version 2 of the 216 | ordinary GNU General Public License has appeared, then you can specify 217 | that version instead if you wish.) Do not make any other change in 218 | these notices. 219 | 220 | Once this change is made in a given copy, it is irreversible for 221 | that copy, so the ordinary GNU General Public License applies to all 222 | subsequent copies and derivative works made from that copy. 223 | 224 | This option is useful when you wish to copy part of the code of 225 | the Library into a program that is not a library. 226 | 227 | 4. You may copy and distribute the Library (or a portion or 228 | derivative of it, under Section 2) in object code or executable form 229 | under the terms of Sections 1 and 2 above provided that you accompany 230 | it with the complete corresponding machine-readable source code, which 231 | must be distributed under the terms of Sections 1 and 2 above on a 232 | medium customarily used for software interchange. 233 | 234 | If distribution of object code is made by offering access to copy 235 | from a designated place, then offering equivalent access to copy the 236 | source code from the same place satisfies the requirement to 237 | distribute the source code, even though third parties are not 238 | compelled to copy the source along with the object code. 239 | 240 | 5. A program that contains no derivative of any portion of the 241 | Library, but is designed to work with the Library by being compiled or 242 | linked with it, is called a "work that uses the Library". Such a 243 | work, in isolation, is not a derivative work of the Library, and 244 | therefore falls outside the scope of this License. 245 | 246 | However, linking a "work that uses the Library" with the Library 247 | creates an executable that is a derivative of the Library (because it 248 | contains portions of the Library), rather than a "work that uses the 249 | library". The executable is therefore covered by this License. 250 | Section 6 states terms for distribution of such executables. 251 | 252 | When a "work that uses the Library" uses material from a header file 253 | that is part of the Library, the object code for the work may be a 254 | derivative work of the Library even though the source code is not. 255 | Whether this is true is especially significant if the work can be 256 | linked without the Library, or if the work is itself a library. The 257 | threshold for this to be true is not precisely defined by law. 258 | 259 | If such an object file uses only numerical parameters, data 260 | structure layouts and accessors, and small macros and small inline 261 | functions (ten lines or less in length), then the use of the object 262 | file is unrestricted, regardless of whether it is legally a derivative 263 | work. (Executables containing this object code plus portions of the 264 | Library will still fall under Section 6.) 265 | 266 | Otherwise, if the work is a derivative of the Library, you may 267 | distribute the object code for the work under the terms of Section 6. 268 | Any executables containing that work also fall under Section 6, 269 | whether or not they are linked directly with the Library itself. 270 | 271 | 6. As an exception to the Sections above, you may also combine or 272 | link a "work that uses the Library" with the Library to produce a 273 | work containing portions of the Library, and distribute that work 274 | under terms of your choice, provided that the terms permit 275 | modification of the work for the customer's own use and reverse 276 | engineering for debugging such modifications. 277 | 278 | You must give prominent notice with each copy of the work that the 279 | Library is used in it and that the Library and its use are covered by 280 | this License. You must supply a copy of this License. If the work 281 | during execution displays copyright notices, you must include the 282 | copyright notice for the Library among them, as well as a reference 283 | directing the user to the copy of this License. Also, you must do one 284 | of these things: 285 | 286 | a) Accompany the work with the complete corresponding 287 | machine-readable source code for the Library including whatever 288 | changes were used in the work (which must be distributed under 289 | Sections 1 and 2 above); and, if the work is an executable linked 290 | with the Library, with the complete machine-readable "work that 291 | uses the Library", as object code and/or source code, so that the 292 | user can modify the Library and then relink to produce a modified 293 | executable containing the modified Library. (It is understood 294 | that the user who changes the contents of definitions files in the 295 | Library will not necessarily be able to recompile the application 296 | to use the modified definitions.) 297 | 298 | b) Use a suitable shared library mechanism for linking with the 299 | Library. A suitable mechanism is one that (1) uses at run time a 300 | copy of the library already present on the user's computer system, 301 | rather than copying library functions into the executable, and (2) 302 | will operate properly with a modified version of the library, if 303 | the user installs one, as long as the modified version is 304 | interface-compatible with the version that the work was made with. 305 | 306 | c) Accompany the work with a written offer, valid for at 307 | least three years, to give the same user the materials 308 | specified in Subsection 6a, above, for a charge no more 309 | than the cost of performing this distribution. 310 | 311 | d) If distribution of the work is made by offering access to copy 312 | from a designated place, offer equivalent access to copy the above 313 | specified materials from the same place. 314 | 315 | e) Verify that the user has already received a copy of these 316 | materials or that you have already sent this user a copy. 317 | 318 | For an executable, the required form of the "work that uses the 319 | Library" must include any data and utility programs needed for 320 | reproducing the executable from it. However, as a special exception, 321 | the materials to be distributed need not include anything that is 322 | normally distributed (in either source or binary form) with the major 323 | components (compiler, kernel, and so on) of the operating system on 324 | which the executable runs, unless that component itself accompanies 325 | the executable. 326 | 327 | It may happen that this requirement contradicts the license 328 | restrictions of other proprietary libraries that do not normally 329 | accompany the operating system. Such a contradiction means you cannot 330 | use both them and the Library together in an executable that you 331 | distribute. 332 | 333 | 7. You may place library facilities that are a work based on the 334 | Library side-by-side in a single library together with other library 335 | facilities not covered by this License, and distribute such a combined 336 | library, provided that the separate distribution of the work based on 337 | the Library and of the other library facilities is otherwise 338 | permitted, and provided that you do these two things: 339 | 340 | a) Accompany the combined library with a copy of the same work 341 | based on the Library, uncombined with any other library 342 | facilities. This must be distributed under the terms of the 343 | Sections above. 344 | 345 | b) Give prominent notice with the combined library of the fact 346 | that part of it is a work based on the Library, and explaining 347 | where to find the accompanying uncombined form of the same work. 348 | 349 | 8. You may not copy, modify, sublicense, link with, or distribute 350 | the Library except as expressly provided under this License. Any 351 | attempt otherwise to copy, modify, sublicense, link with, or 352 | distribute the Library is void, and will automatically terminate your 353 | rights under this License. However, parties who have received copies, 354 | or rights, from you under this License will not have their licenses 355 | terminated so long as such parties remain in full compliance. 356 | 357 | 9. You are not required to accept this License, since you have not 358 | signed it. However, nothing else grants you permission to modify or 359 | distribute the Library or its derivative works. These actions are 360 | prohibited by law if you do not accept this License. Therefore, by 361 | modifying or distributing the Library (or any work based on the 362 | Library), you indicate your acceptance of this License to do so, and 363 | all its terms and conditions for copying, distributing or modifying 364 | the Library or works based on it. 365 | 366 | 10. Each time you redistribute the Library (or any work based on the 367 | Library), the recipient automatically receives a license from the 368 | original licensor to copy, distribute, link with or modify the Library 369 | subject to these terms and conditions. You may not impose any further 370 | restrictions on the recipients' exercise of the rights granted herein. 371 | You are not responsible for enforcing compliance by third parties with 372 | this License. 373 | 374 | 11. If, as a consequence of a court judgment or allegation of patent 375 | infringement or for any other reason (not limited to patent issues), 376 | conditions are imposed on you (whether by court order, agreement or 377 | otherwise) that contradict the conditions of this License, they do not 378 | excuse you from the conditions of this License. If you cannot 379 | distribute so as to satisfy simultaneously your obligations under this 380 | License and any other pertinent obligations, then as a consequence you 381 | may not distribute the Library at all. For example, if a patent 382 | license would not permit royalty-free redistribution of the Library by 383 | all those who receive copies directly or indirectly through you, then 384 | the only way you could satisfy both it and this License would be to 385 | refrain entirely from distribution of the Library. 386 | 387 | If any portion of this section is held invalid or unenforceable under any 388 | particular circumstance, the balance of the section is intended to apply, 389 | and the section as a whole is intended to apply in other circumstances. 390 | 391 | It is not the purpose of this section to induce you to infringe any 392 | patents or other property right claims or to contest validity of any 393 | such claims; this section has the sole purpose of protecting the 394 | integrity of the free software distribution system which is 395 | implemented by public license practices. Many people have made 396 | generous contributions to the wide range of software distributed 397 | through that system in reliance on consistent application of that 398 | system; it is up to the author/donor to decide if he or she is willing 399 | to distribute software through any other system and a licensee cannot 400 | impose that choice. 401 | 402 | This section is intended to make thoroughly clear what is believed to 403 | be a consequence of the rest of this License. 404 | 405 | 12. If the distribution and/or use of the Library is restricted in 406 | certain countries either by patents or by copyrighted interfaces, the 407 | original copyright holder who places the Library under this License may add 408 | an explicit geographical distribution limitation excluding those countries, 409 | so that distribution is permitted only in or among countries not thus 410 | excluded. In such case, this License incorporates the limitation as if 411 | written in the body of this License. 412 | 413 | 13. The Free Software Foundation may publish revised and/or new 414 | versions of the Lesser General Public License from time to time. 415 | Such new versions will be similar in spirit to the present version, 416 | but may differ in detail to address new problems or concerns. 417 | 418 | Each version is given a distinguishing version number. If the Library 419 | specifies a version number of this License which applies to it and 420 | "any later version", you have the option of following the terms and 421 | conditions either of that version or of any later version published by 422 | the Free Software Foundation. If the Library does not specify a 423 | license version number, you may choose any version ever published by 424 | the Free Software Foundation. 425 | 426 | 14. If you wish to incorporate parts of the Library into other free 427 | programs whose distribution conditions are incompatible with these, 428 | write to the author to ask for permission. For software which is 429 | copyrighted by the Free Software Foundation, write to the Free 430 | Software Foundation; we sometimes make exceptions for this. Our 431 | decision will be guided by the two goals of preserving the free status 432 | of all derivatives of our free software and of promoting the sharing 433 | and reuse of software generally. 434 | 435 | NO WARRANTY 436 | 437 | 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO 438 | WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. 439 | EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR 440 | OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY 441 | KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE 442 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 443 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE 444 | LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME 445 | THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 446 | 447 | 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN 448 | WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY 449 | AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU 450 | FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR 451 | CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE 452 | LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING 453 | RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A 454 | FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF 455 | SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH 456 | DAMAGES. 457 | 458 | END OF TERMS AND CONDITIONS 459 | 460 | How to Apply These Terms to Your New Libraries 461 | 462 | If you develop a new library, and you want it to be of the greatest 463 | possible use to the public, we recommend making it free software that 464 | everyone can redistribute and change. You can do so by permitting 465 | redistribution under these terms (or, alternatively, under the terms of the 466 | ordinary General Public License). 467 | 468 | To apply these terms, attach the following notices to the library. It is 469 | safest to attach them to the start of each source file to most effectively 470 | convey the exclusion of warranty; and each file should have at least the 471 | "copyright" line and a pointer to where the full notice is found. 472 | 473 | 474 | Copyright (C) 475 | 476 | This library is free software; you can redistribute it and/or 477 | modify it under the terms of the GNU Lesser General Public 478 | License as published by the Free Software Foundation; either 479 | version 2.1 of the License, or (at your option) any later version. 480 | 481 | This library is distributed in the hope that it will be useful, 482 | but WITHOUT ANY WARRANTY; without even the implied warranty of 483 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 484 | Lesser General Public License for more details. 485 | 486 | You should have received a copy of the GNU Lesser General Public 487 | License along with this library; if not, write to the Free Software 488 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 489 | USA 490 | 491 | Also add information on how to contact you by electronic and paper mail. 492 | 493 | You should also get your employer (if you work as a programmer) or your 494 | school, if any, to sign a "copyright disclaimer" for the library, if 495 | necessary. Here is a sample; alter the names: 496 | 497 | Yoyodyne, Inc., hereby disclaims all copyright interest in the 498 | library `Frob' (a library for tweaking knobs) written by James Random 499 | Hacker. 500 | 501 | , 1 April 1990 502 | Ty Coon, President of Vice 503 | 504 | That's all there is to it! 505 | --------------------------------------------------------------------------------