package moe.nea.pcj; import org.jspecify.annotations.Nullable; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Optional; import java.util.function.Function; public sealed interface Result permits Result.Ok, Result.Fail { default boolean isOk() { return errors().isEmpty(); } Optional value(); Optional partial(); default Optional valueOrPartial() { return value().or(this::partial); } List errors(); default Result map(Function mapper) { return flatMap(mapper.andThen(Result::ok)); } Result flatMap(Function> mapper); default Result mapError(Function mapper) { return mapErrors(it -> it.stream().map(mapper).toList()); } Result mapErrors(Function, List> mapper); Result appendErrors(List error); record Ok(Good okValue) implements Result { @Override public Result appendErrors(List errors) { return new Fail<>(okValue, errors); } @Override public Result mapErrors(Function, List> mapper) { return new Ok<>(okValue); } @Override public Optional partial() { return Optional.empty(); } @Override public List errors() { return List.of(); } @Override public Result flatMap(Function> mapper) { return Result.cast(mapper.apply(okValue)); } @Override public Optional value() { return Optional.of(okValue); } } record Fail(@Nullable Good partialValue, List badValue) implements Result { public Fail { if (badValue.isEmpty()) throw new IllegalArgumentException("Cannot create failure without any error values"); } @Override public Optional value() { return Optional.empty(); } @Override public Optional partial() { return Optional.ofNullable(partialValue); } @Override public List errors() { return Collections.unmodifiableList(badValue); } @Override public Result flatMap(Function> mapper) { if (partialValue != null) { return Result.cast(mapper.apply(partialValue)).appendErrors(badValue); } return new Fail<>(null, badValue); } @Override public Result mapErrors(Function, List> mapper) { return new Fail<>(partialValue, mapper.apply(badValue)); } @Override public Result appendErrors(List errors) { var nextErrors = new ArrayList<>(badValue); nextErrors.addAll(errors); return new Fail<>(partialValue, nextErrors); } } static Result ok(Good value) { return new Ok<>(value); } static Result.Fail fail(Bad error) { return new Fail<>(null, List.of(error)); } static Result cast(Result c) { //noinspection unchecked return (Result) c; } static Result.Fail partial(@Nullable Good partial, Bad error) { return new Fail<>(partial, List.of(error)); } }