Skip to content

Instantly share code, notes, and snippets.

@ToastShaman
Created October 17, 2024 15:04
Show Gist options
  • Save ToastShaman/6ad4e86cf967ccf04b141d0c5bb30934 to your computer and use it in GitHub Desktop.
Save ToastShaman/6ad4e86cf967ccf04b141d0c5bb30934 to your computer and use it in GitHub Desktop.
package com.github.toastshaman.json.reader;
import io.vavr.Function0;
import io.vavr.Function1;
import io.vavr.Function2;
import io.vavr.Function3;
import io.vavr.Function4;
import org.json.JSONObject;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Stream;
import static java.util.Objects.requireNonNull;
public final class JsonReader<T> {
private final Function1<JSONObject, T> f;
public JsonReader(Function1<JSONObject, T> f) {
this.f = requireNonNull(f);
}
public T apply(JSONObject json) {
return f.apply(requireNonNull(json));
}
public <R> JsonReader<R> map(Function1<T, R> mapper) {
return new JsonReader<>(root -> {
var value = f.apply(root);
return value == null ? null : mapper.apply(value);
});
}
public <R> JsonReader<R> flatMap(Function1<T, JsonReader<R>> mapper) {
return new JsonReader<>(root -> {
var value = f.apply(root);
if (value == null) return null;
JsonReader<R> reader = mapper.apply(value);
if (reader == null) return null;
return reader.apply(root);
});
}
public JsonReader<T> orElse(T defaultValue) {
return new JsonReader<>(f.andThen(value -> value == null ? defaultValue : value));
}
public <X extends Throwable> JsonReader<T> orElseThrow(Function0<? extends X> exceptionSupplier) {
return new JsonReader<>(root -> {
var value = f.apply(root);
if (value == null) {
throw new JsonReaderException(exceptionSupplier.get());
}
return value;
});
}
public JsonReader<T> or(JsonReader<T> fallback) {
return new JsonReader<>(root -> {
var value = f.apply(root);
return value == null ? fallback.apply(root) : value;
});
}
public static <T> JsonReader<T> of(Function1<JSONObject, T> f) {
return new JsonReader<>(f);
}
public static <T> JsonReader<T> pure(T value) {
return new JsonReader<>(_ -> value);
}
public static <T> JsonReader<T> required(String key, Function2<JSONObject, String, T> extractor) {
return new JsonReader<>(root -> {
var exists = root.has(key) || root.isNull(key);
if (!exists) {
throw new JsonReaderException(new IllegalArgumentException("Missing required field: %s".formatted(key)));
}
return extractor.apply(root, key);
});
}
public static <T1, T2, R> JsonReader<R> zip(JsonReader<T1> reader1,
JsonReader<T2> reader2,
Function2<T1, T2, R> zipper) {
return new JsonReader<>(root -> {
var value1 = reader1.apply(root);
var value2 = reader2.apply(root);
return zipper.apply(value1, value2);
});
}
public static <T1, T2, T3, R> JsonReader<R> zip(JsonReader<T1> reader1,
JsonReader<T2> reader2,
JsonReader<T3> reader3,
Function3<T1, T2, T3, R> zipper) {
return new JsonReader<>(root -> {
var value1 = reader1.apply(root);
var value2 = reader2.apply(root);
var value3 = reader3.apply(root);
return zipper.apply(value1, value2, value3);
});
}
public static <T1, T2, T3, T4, R> JsonReader<R> zip(JsonReader<T1> reader1,
JsonReader<T2> reader2,
JsonReader<T3> reader3,
JsonReader<T4> reader4,
Function4<T1, T2, T3, T4, R> zipper) {
return new JsonReader<>(root -> {
var value1 = reader1.apply(root);
var value2 = reader2.apply(root);
var value3 = reader3.apply(root);
var value4 = reader4.apply(root);
return zipper.apply(value1, value2, value3, value4);
});
}
public static <T> JsonReader<List<T>> all(JsonReader<T> first, JsonReader<T>... rest) {
return new JsonReader<>(root -> Stream.concat(Stream.of(first), Arrays.stream(rest)).map(reader -> reader.apply(root)).toList());
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment