فهرست منبع

Add support for a Result<T, E> type, which provides a functional wrapper for throwing functions.

Sam Jaffe 5 سال پیش
والد
کامیت
7c54a6f05f

+ 1 - 1
pom.xml

@@ -92,5 +92,5 @@
       <version>1.18.16</version>
     </dependency>
 	</dependencies>
-	<version>0.2</version>
+	<version>0.3.0</version>
 </project>

+ 63 - 0
src/main/lombok/org/leumasjaffe/container/functional/Result.java

@@ -0,0 +1,63 @@
+package org.leumasjaffe.container.functional;
+
+import java.util.Objects;
+import java.util.function.Consumer;
+import java.util.function.Function;
+import java.util.function.Supplier;
+
+import org.leumasjaffe.container.Either;
+
+import lombok.AccessLevel;
+import lombok.AllArgsConstructor;
+import lombok.experimental.FieldDefaults;
+
+@FieldDefaults(level=AccessLevel.PRIVATE, makeFinal=true)
+@AllArgsConstructor(access=AccessLevel.PRIVATE)
+public class Result<T, E extends Exception> {
+	Either<T, E> impl;
+	
+	public static <T, E extends Exception> Result<T, E> of(T value) {
+		return new Result<>(Either.ofLeft(value));
+	}
+	
+	public static <T, E extends Exception> Result<T, E> except(E exception) {
+		return new Result<>(Either.ofRight(exception));
+	}
+	
+	@SuppressWarnings("unchecked")
+	public static <T, E extends Exception> Result<T, E> maybe(ThrowingSupplier<T, E> supplier) {
+		T value = null;
+		Objects.requireNonNull(supplier);
+		try {
+			value = supplier.get();
+		} catch (Exception exception) {
+			return except((E) exception);
+		}
+		return of(value);
+	}
+	
+	public static <T, R, E extends Exception> Function<T, Result<R, E>> maybe(ThrowingFunction<T, R, E> function) {
+		Objects.requireNonNull(function);
+		return input -> maybe(() -> function.apply(input));
+	}
+	
+	public <R> Result<R, E> map(final Function<T, R> transform) {
+		return new Result<>(impl.mapLeft(transform));
+	}
+
+	public T orNull(final Consumer<E> handler) {
+		return impl.unify(t -> t, (e) -> { handler.accept(e); return null; });
+	}
+
+	public T or(final T ifException, final Consumer<E> handler) {
+		return impl.unify(t -> t, (e) -> { handler.accept(e); return ifException; });
+	}
+	
+	public T or(final Supplier<T> ifException, final Consumer<E> handler) {
+		return impl.unify(t -> t, (e) -> { handler.accept(e); return ifException.get(); });
+	}
+
+	public void consume(final Consumer<T> success, final Consumer<E> failure) {
+		impl.consume(success, failure);
+	}
+}

+ 6 - 0
src/main/lombok/org/leumasjaffe/container/functional/ThrowingFunction.java

@@ -0,0 +1,6 @@
+package org.leumasjaffe.container.functional;
+
+@FunctionalInterface
+public interface ThrowingFunction<T, R, E extends Exception> {
+	R apply(T t) throws E;
+}

+ 6 - 0
src/main/lombok/org/leumasjaffe/container/functional/ThrowingSupplier.java

@@ -0,0 +1,6 @@
+package org.leumasjaffe.container.functional;
+
+@FunctionalInterface
+public interface ThrowingSupplier<R, E extends Exception> {
+	R get() throws E;
+}

+ 74 - 0
src/test/java/org/leumasjaffe/container/functional/ResultStateTest.java

@@ -0,0 +1,74 @@
+package org.leumasjaffe.container.functional;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+import java.util.function.Function;
+
+import org.junit.jupiter.api.Test;
+
+class ResultStateTest {
+	
+	@Test
+	void testMaybeFunctionProducesResultGenerator() {
+		final Function<Void, Result<Object, Exception>> gen = Result.maybe(v -> new Object());
+		gen.apply(null).consume(t -> assertNotNull(t), e -> fail(e));
+	}
+	
+	@Test
+	void testCapturesValueInSupplier() {
+		final Object obj = new Object();
+		assertDoesNotThrow(() -> Result.maybe(() -> obj));
+		assertSame(obj, Result.maybe(() -> obj).orNull((e) -> fail(e)));
+	}
+	
+	@Test
+	void testOrOnValueReturnsValue() {
+		final Object obj = new Object();
+		assertDoesNotThrow(() -> Result.maybe(() -> obj));
+		assertSame(obj, Result.of(obj).orNull((e) -> fail(e)));
+		assertSame(obj, Result.of(obj).or(new Object(), (e) -> fail(e)));
+		assertSame(obj, Result.of(obj).or(Object::new, (e) -> fail(e)));
+	}
+	
+	@Test
+	void testCanTransformObjectWithMap() {
+		final Object obj = new Object();
+		Result.of(obj).map(Object::hashCode)
+			.consume(t -> assertEquals(obj.hashCode(), t), e -> fail(e));
+	}
+	
+	@Test
+	void testMapExceptionIsNoOp() {
+		Result.except(new Exception()).map(Object::hashCode)
+			.consume(t -> fail(), e -> {});
+	}
+
+	@Test
+	void testCapturesThrowInSupplier() {
+		Exception t = new Exception();
+		assertDoesNotThrow(() -> Result.maybe(() -> { throw new Exception(); } ));
+		Object out = Result.except(t).orNull((e) -> { assertSame(t, e); });
+		assertNull(out);
+	}
+
+	@Test
+	void testCanProvideReplacementWithOr() {
+		Object out = Result.except(new Exception()).or(new Object(), (e) -> {});
+		assertNotNull(out);
+	}
+
+	@Test
+	void testCanProvideSupplierWithOr() {
+		Object out = Result.except(new Exception()).or(Object::new, (e) -> {});
+		assertNotNull(out);
+	}
+
+	@Test
+	void testCanConsume() {
+		Result<Object, Exception> good = Result.of(new Object());
+		Result<Object, Exception> bad = Result.except(new Exception());
+		good.consume((t) -> {}, (e) -> fail(e));
+		bad.consume((t) -> fail(), (e) -> {});
+	}
+
+}

+ 34 - 0
src/test/java/org/leumasjaffe/container/functional/ResultValidationTest.java

@@ -0,0 +1,34 @@
+package org.leumasjaffe.container.functional;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+import org.junit.jupiter.api.Test;
+
+class ResultValidationTest {
+
+	@Test
+	void testResultOfThrows() {
+		assertThrows(NullPointerException.class, () -> Result.of(null));
+	}
+
+	@Test
+	void testResultExceptThrows() {
+		assertThrows(NullPointerException.class, () -> Result.except(null));
+	}
+
+	@Test
+	void testResultMaybeThrows() {
+		ThrowingSupplier<Object, Exception> supplier = null;
+		ThrowingFunction<Object, Object, Exception> function = null;
+		assertThrows(NullPointerException.class, () -> Result.maybe(supplier));
+		assertThrows(NullPointerException.class, () -> Result.maybe(() -> null));
+		assertThrows(NullPointerException.class, () -> Result.maybe(function));
+	}
+
+	@Test
+	void testMapThrows() {
+		final Result<Object, Exception> result = Result.of(new Object());
+		assertThrows(NullPointerException.class, () -> result.map(null));
+		assertThrows(NullPointerException.class, () -> result.map(o -> null));
+	}
+}