import Either; | |
import lombok.AccessLevel; | |
import lombok.AllArgsConstructor; | |
import lombok.NoArgsConstructor; | |
import lombok.ToString; | |
import java.util.*; | |
import java.util.function.BiFunction; | |
import java.util.function.Function; | |
import java.util.function.Supplier; | |
@NoArgsConstructor(access = AccessLevel.PRIVATE) | |
@AllArgsConstructor(access = AccessLevel.PRIVATE) | |
@ToString | |
public class Outcome<T> { | |
private Either<List<Failure>, Optional<T>> either; | |
public static <T> Outcome<T> maybe(Optional<T> maybeSuccess) { | |
Outcome<T> result = new Outcome<>(); | |
result.either = Either.right(maybeSuccess); | |
return result; | |
} | |
public static <T> Outcome<List<T>> sequence(List<Outcome<T>> outcomes){ | |
final Outcome<List<T>> unit = Outcome.maybe(new ArrayList<>(outcomes.size())); | |
return outcomes | |
.stream() | |
.reduce(unit, | |
//Extract T instance and add it to unit, otherwise combine errors | |
(o1, o2) -> | |
o1.flatMapR( | |
l -> o2.mapR(ec -> { | |
l.add(ec); | |
return l; | |
}) | |
).dependingOn(o2), | |
//Merge all results into one | |
(ol1, ol2) -> | |
ol1.flatMapR(l1 -> ol2.mapR(l2 -> { | |
final List<T> ll = new ArrayList<>(l1.size() + l2.size()); | |
ll.addAll(l1); | |
ll.addAll(l2); | |
return ll; | |
}))); | |
} | |
public static <T> Outcome<T> maybe(T maybeSuccess) { | |
return maybe(Optional.ofNullable(maybeSuccess)); | |
} | |
public static <T> Outcome<T> emptyMaybe() { | |
return maybe(Optional.<T>empty()); | |
} | |
public static <T> Outcome<T> failure(final List<Failure> failures) { | |
Outcome<T> result = new Outcome<>(); | |
result.either = Either.left(failures); | |
return result; | |
} | |
public static <T> Outcome<T> failure(final Failure failure) { | |
Outcome<T> result = new Outcome<>(); | |
result.either = Either.left(Arrays.asList(failure)); | |
return result; | |
} | |
public boolean isMaybe() { | |
return either.isRight(); | |
} | |
public boolean isMaybePresent() { | |
if (either.isRight()) { | |
return either.getRight().isPresent(); | |
} else { | |
return false; | |
} | |
} | |
public boolean isFailure() { | |
return either.isLeft(); | |
} | |
public T maybe() { | |
if (either.isRight() && either.getRight().isPresent()) { | |
return either.getRight().get(); | |
} else { | |
throw new NoSuchElementException("No value present"); | |
} | |
} | |
public List<Failure> failures() { | |
if (either.isLeft()) { | |
return either.getLeft(); | |
} else { | |
throw new NoSuchElementException("No value present"); | |
} | |
} | |
public final <TT> Outcome<TT> mapR(Function<T, TT> transform) { | |
if (isMaybe()) { | |
return maybe(this.either.getRight().map(transform)); | |
} else { | |
return (Outcome<TT>) this; | |
} | |
} | |
public final <TT> Outcome<TT> mapL(Function<List<Failure>, List<Failure>> add) { | |
if (isFailure()) { | |
return (Outcome<TT>) failure(add.apply(failures())); | |
} else { | |
return (Outcome<TT>) this; | |
} | |
} | |
public final <TT> Outcome<TT> flatMapR(Function<T, Outcome<TT>> function) { | |
if (isMaybe() && this.either.getRight().isPresent()) { | |
return function.apply(this.either.getRight().get()); | |
} else { | |
return (Outcome<TT>) this; | |
} | |
} | |
public final <TT> Outcome<TT> inferred() | |
{ | |
return (Outcome<TT>)this; | |
} | |
public final <U> Outcome<T> dependingOn(Outcome<U> that) { | |
if (this.isFailure() || that.isFailure()) { | |
if(this.isFailure() && that.isFailure()){ | |
List<Failure> failures = new ArrayList<>(that.failures()); | |
//Add only those failures that doesn't exist (avoid repeated values) | |
failures() | |
.stream() | |
.filter(f -> !failures.contains(f)) | |
.forEach(failures::add); | |
return failure(failures); | |
} | |
else if(that.isFailure()) | |
{ | |
return (Outcome<T>)that; | |
} | |
else | |
{ | |
return this; | |
} | |
} | |
else { | |
return this; | |
} | |
} | |
public final <TT> Outcome<TT> fold(Function<List<Failure>, TT> failure, Function<T, TT> success) { | |
if (isMaybe()) { | |
return maybe(this.either.getRight().map(success)); | |
} else { | |
return maybe(Optional.ofNullable(failure.apply(this.either.getLeft()))); | |
} | |
} | |
public final T orElseGet(Supplier<T> otherValue) { | |
if (isMaybe() && either.getRight().isPresent()) { | |
return either.getRight().get(); | |
} else { | |
return otherValue.get(); | |
} | |
} | |
public final <TT> Outcome<TT> orIfEmpty(Supplier<Failure> failure) { | |
if (isMaybePresent()) { | |
return (Outcome<TT>)this; | |
} else { | |
final ArrayList<Failure> newfailures; | |
if(isFailure()) { | |
newfailures = new ArrayList<>(failures()); | |
newfailures.add(failure.get()); | |
} | |
else | |
{ | |
newfailures = new ArrayList<>(1); | |
newfailures.add(failure.get()); | |
} | |
return failure(newfailures); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment