net.hamnaberg.json.codec.DecodeResult Maven / Gradle / Ivy
The newest version!
package net.hamnaberg.json.codec;
import net.hamnaberg.json.Json;
import java.util.ArrayList;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.Optional;
import java.util.concurrent.Callable;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;
import java.util.stream.Collector;
public abstract class DecodeResult {
private DecodeResult() {
}
public final DecodeResult map(Function f) {
return fold(DecodeResult::fail, f.andThen(DecodeResult::ok));
}
public final DecodeResult filter(Predicate p) {
return filter(p, () -> "Filter failed");
}
public final DecodeResult filter(Predicate p, Supplier errorSupplier) {
return flatMap(a -> p.test(a) ? ok(a) : fail(errorSupplier.get()));
}
public final void forEach(Consumer f) {
consume(ignore -> {}, f);
}
public final DecodeResult flatMap(Function> f) {
return fold(DecodeResult::fail, f);
}
public final A getOrElse(Supplier orElse) {
return fold(ignore -> orElse.get(), Function.identity());
}
public final DecodeResult orElse(DecodeResult orThis) {
return isOk() ? this : orThis;
}
public abstract A getOrElseThrow(Function exProvider) throws X;
public final A unsafeGet() {
return fold(e -> {
throw new NoSuchElementException(e);
}, Function.identity());
}
public final Optional toOption() {
return fold(ignore -> Optional.empty(), Optional::of);
}
public abstract B fold(Function failFunction, Function okFunction);
public abstract void consume(Consumer failFunction, Consumer okFunction);
public boolean isOk() {
return fold(a -> false, a -> true);
}
public boolean isFailure() {
return fold(a -> true, a -> false);
}
public static DecodeResult> sequence(List> decodeResults) {
if (decodeResults.isEmpty()) {
return DecodeResult.ok(List.of());
}
final List list = new ArrayList<>();
final List errors = new ArrayList<>();
for (DecodeResult res : decodeResults) {
res.consume(errors::add, list::add);
}
if (errors.isEmpty()) {
return DecodeResult.ok(List.copyOf(list));
} else {
return DecodeResult.fail("One or more results failed: " + String.join("\n", errors));
}
}
public static DecodeResult sequence(final Iterable> results, final Collector collector) {
A accumulator = collector.supplier().get();
for (final DecodeResult t : results) {
if (t.isFailure()) {
return fail(t.fold(Function.identity(), x -> {
throw new NoSuchElementException();
}));
}
collector.accumulator().accept(accumulator, t.fold(f -> {
throw new NoSuchElementException();
}, Function.identity()));
}
return ok(collector.finisher().apply(accumulator));
}
public static DecodeResult ok(A value) {
return new Ok<>(value);
}
public static DecodeResult fromOption(Optional value) {
return fromOption(value, () -> "No value found");
}
public static DecodeResult fromCallable(Callable value) {
try {
return ok(value.call());
} catch (Exception e) {
return fail(e.getMessage());
}
}
public static DecodeResult fromOption(Optional value, Supplier error) {
return value.map(DecodeResult::ok).orElse(fail(error.get()));
}
@SuppressWarnings("unchecked")
public static DecodeResult fail(String message) {
return (DecodeResult) new Failure(message);
}
private static DecodeResult getValue(Json.JObject object, String name) {
return object.
get(name).
map(DecodeResult::ok).
orElseGet(() -> DecodeResult.fail(String.format("'%s' not found in %s", name, object.nospaces())));
}
public static DecodeResult decode(Json.JObject object, FieldDecoder decoder) {
return decode(object, decoder.name, decoder.decoder);
}
public static DecodeResult decode(Json.JObject object, String name, DecodeJson decoder) {
DecodeResult result = getValue(object, name).flatMap(decoder::fromJson);
if (result.isFailure()) {
Optional defaultValue = decoder.defaultValue();
result = defaultValue.map(DecodeResult::ok).orElse(result);
if (result.isFailure()) {
result = result.fold(
err -> DecodeResult.fail(err.contains(String.format("'%s' not found", name)) ? err : String.format("'%s' failed with message: '%s'", name, err)),
DecodeResult::ok
);
}
}
return result;
}
public final static class Ok extends DecodeResult {
public final A value;
public Ok(A value) {
this.value = value;
}
@Override
public B fold(Function failFunction, Function okFunction) {
return okFunction.apply(this.value);
}
@Override
public void consume(Consumer failFunction, Consumer okFunction) {
okFunction.accept(this.value);
}
@Override
public A getOrElseThrow(Function exProvider) throws X {
return value;
}
public A getValue() {
return value;
}
@Override
public String toString() {
return String.format("Ok(value='%s')", value);
}
}
public final static class Failure extends DecodeResult
© 2015 - 2024 Weber Informatics LLC | Privacy Policy