package scotch.util; import static lombok.AccessLevel.PRIVATE; import java.util.function.Function; import lombok.AllArgsConstructor; import lombok.EqualsAndHashCode; import lombok.ToString; public abstract class Either<A, B> { public static <A, B> Either<A, B> left(A left) { return new Left<>(left); } public static <A, B> Either<A, B> right(B right) { return new Right<>(right); } private Either() { // intentionally empty } @Override public abstract boolean equals(Object o); public abstract A getLeft(); public abstract B getRight(); @Override public abstract int hashCode(); public boolean isLeft() { return !isRight(); } public abstract boolean isRight(); public abstract <C> Either<A, C> map(Function<B, C> function); public abstract B orElseGet(Function<A, B> function); public abstract <T extends RuntimeException> B orElseThrow(Function<A, T> function) throws T; @Override public abstract String toString(); @AllArgsConstructor(access = PRIVATE) @EqualsAndHashCode(callSuper = false) @ToString public static class Left<A, B> extends Either<A, B> { private final A value; @Override public A getLeft() { return value; } @Override public B getRight() { throw new IllegalStateException(); } @Override public boolean isRight() { return false; } @SuppressWarnings("unchecked") @Override public <C> Either<A, C> map(Function<B, C> function) { return (Either<A, C>) this; } @Override public B orElseGet(Function<A, B> function) { return function.apply(value); } @Override public <T extends RuntimeException> B orElseThrow(Function<A, T> function) throws T { throw function.apply(value); } } @AllArgsConstructor(access = PRIVATE) @EqualsAndHashCode(callSuper = false) @ToString public static class Right<A, B> extends Either<A, B> { private final B value; @Override public A getLeft() { throw new IllegalStateException(); } @Override public B getRight() { return value; } @Override public boolean isRight() { return true; } @Override public <C> Either<A, C> map(Function<B, C> function) { return right(function.apply(value)); } @Override public B orElseGet(Function<A, B> function) { return value; } @Override public <T extends RuntimeException> B orElseThrow(Function<A, T> function) throws T { return value; } } }