summaryrefslogtreecommitdiff
path: root/src/main/java/moe/nea/pcj/json
diff options
context:
space:
mode:
authorLinnea Gräf <nea@nea.moe>2024-11-14 01:35:24 +0100
committerLinnea Gräf <nea@nea.moe>2024-11-14 01:35:24 +0100
commited8eb00566a5e3b7f2d5564d7977a4b3ec8fe5b3 (patch)
tree9f8418ce1f27634a5a5f37a07f57c45379584d87 /src/main/java/moe/nea/pcj/json
parent94e30f7ffbafcdd188d09d8c7bf7e4794b650018 (diff)
downloadprofunctor-codecs-java-ed8eb00566a5e3b7f2d5564d7977a4b3ec8fe5b3.tar.gz
profunctor-codecs-java-ed8eb00566a5e3b7f2d5564d7977a4b3ec8fe5b3.tar.bz2
profunctor-codecs-java-ed8eb00566a5e3b7f2d5564d7977a4b3ec8fe5b3.zip
wip
Diffstat (limited to 'src/main/java/moe/nea/pcj/json')
-rw-r--r--src/main/java/moe/nea/pcj/json/AtField.java4
-rw-r--r--src/main/java/moe/nea/pcj/json/BasicCodecs.java51
-rw-r--r--src/main/java/moe/nea/pcj/json/DuplicateJsonKey.java4
-rw-r--r--src/main/java/moe/nea/pcj/json/JsonCodec.java31
-rw-r--r--src/main/java/moe/nea/pcj/json/JsonLikeError.java4
-rw-r--r--src/main/java/moe/nea/pcj/json/JsonLikeOperations.java32
-rw-r--r--src/main/java/moe/nea/pcj/json/ListBuilder.java9
-rw-r--r--src/main/java/moe/nea/pcj/json/ListView.java14
-rw-r--r--src/main/java/moe/nea/pcj/json/MapCodec.java17
-rw-r--r--src/main/java/moe/nea/pcj/json/MissingKey.java4
-rw-r--r--src/main/java/moe/nea/pcj/json/RecordBuilder.java12
-rw-r--r--src/main/java/moe/nea/pcj/json/RecordCodec.java59
-rw-r--r--src/main/java/moe/nea/pcj/json/RecordView.java10
-rw-r--r--src/main/java/moe/nea/pcj/json/UnexpectedJsonElement.java7
-rw-r--r--src/main/java/moe/nea/pcj/json/package-info.java4
15 files changed, 262 insertions, 0 deletions
diff --git a/src/main/java/moe/nea/pcj/json/AtField.java b/src/main/java/moe/nea/pcj/json/AtField.java
new file mode 100644
index 0000000..3780e38
--- /dev/null
+++ b/src/main/java/moe/nea/pcj/json/AtField.java
@@ -0,0 +1,4 @@
+package moe.nea.pcj.json;
+
+public record AtField(String field, JsonLikeError error) implements JsonLikeError {
+}
diff --git a/src/main/java/moe/nea/pcj/json/BasicCodecs.java b/src/main/java/moe/nea/pcj/json/BasicCodecs.java
new file mode 100644
index 0000000..8f957f9
--- /dev/null
+++ b/src/main/java/moe/nea/pcj/json/BasicCodecs.java
@@ -0,0 +1,51 @@
+package moe.nea.pcj.json;
+
+import moe.nea.pcj.Result;
+
+public class BasicCodecs<Format> {
+ protected BasicCodecs() {}
+
+ public static <Format> BasicCodecs<Format> create() {
+ return new BasicCodecs<>();
+ }
+
+ public final JsonCodec<String, Format> STRING = new JsonCodec<>() {
+ @Override
+ public Result<String, ? extends JsonLikeError> decode(Format format, JsonLikeOperations<Format> ops) {
+ return ops.getString(format);
+ }
+
+ @Override
+ public Result<Format, ? extends JsonLikeError> encode(String data, JsonLikeOperations<Format> ops) {
+ return Result.ok(ops.createString(data));
+ }
+ };
+
+ public final JsonCodec<Float, Format> FLOAT = new JsonCodec<Float, Format>() {
+ @Override
+ public Result<? extends Float, ? extends JsonLikeError> decode(Format format, JsonLikeOperations<Format> ops) {
+ return ops.getNumeric(format).map(Number::floatValue);
+ }
+
+ @Override
+ public Result<? extends Format, ? extends JsonLikeError> encode(Float data, JsonLikeOperations<Format> ops) {
+ return Result.ok(ops.createNumeric(data));
+ }
+ };
+
+ public final JsonCodec<Integer, Format> INTEGER = new JsonCodec<>() {
+ @Override
+ public Result<? extends Integer, ? extends JsonLikeError> decode(Format format, JsonLikeOperations<Format> ops) {
+ return ops.getNumeric(format).map(Number::intValue); // TODO: filter for valid ints
+ }
+
+ @Override
+ public Result<? extends Format, ? extends JsonLikeError> encode(Integer data, JsonLikeOperations<Format> ops) {
+ return Result.ok(ops.createNumeric(data));
+ }
+ };
+
+ public <T> JsonCodec<T, Format> objectCodec() {
+
+ }
+} \ No newline at end of file
diff --git a/src/main/java/moe/nea/pcj/json/DuplicateJsonKey.java b/src/main/java/moe/nea/pcj/json/DuplicateJsonKey.java
new file mode 100644
index 0000000..13d81db
--- /dev/null
+++ b/src/main/java/moe/nea/pcj/json/DuplicateJsonKey.java
@@ -0,0 +1,4 @@
+package moe.nea.pcj.json;
+
+public record DuplicateJsonKey(String key) implements JsonLikeError {
+}
diff --git a/src/main/java/moe/nea/pcj/json/JsonCodec.java b/src/main/java/moe/nea/pcj/json/JsonCodec.java
new file mode 100644
index 0000000..65f4a5e
--- /dev/null
+++ b/src/main/java/moe/nea/pcj/json/JsonCodec.java
@@ -0,0 +1,31 @@
+package moe.nea.pcj.json;
+
+import moe.nea.pcj.Codec;
+import moe.nea.pcj.Result;
+
+public interface JsonCodec<T, Format> extends Codec<
+ T, Format, JsonLikeOperations<Format>,
+ JsonLikeError, JsonLikeError> {
+
+ default MapCodec<T, Format> fieldOf(String key) {
+ return new MapCodec<>() {
+ @Override
+ public Result<T, JsonLikeError> decode(RecordView<Format> record, JsonLikeOperations<Format> ops) {
+ return record.get(key)
+ .map(element -> Result.<T, JsonLikeError>cast(
+ JsonCodec.this.decode(element, ops)
+ .mapError(it -> new AtField(key, it))))
+ .orElseGet(() -> Result.fail(new MissingKey(key)));
+ }
+
+ @Override
+ public Result<RecordBuilder<Format>, JsonLikeError> encode(T value, JsonLikeOperations<Format> ops) {
+ var record = ops.createObject();
+ return Result.<Format, JsonLikeError>cast(JsonCodec.this.encode(value, ops))
+ .<JsonLikeError>mapError(it -> new AtField(key, it))
+ .flatMap(json -> Result.cast(record.add(key, json).map(unit -> record)));
+ }
+
+ };
+ }
+}
diff --git a/src/main/java/moe/nea/pcj/json/JsonLikeError.java b/src/main/java/moe/nea/pcj/json/JsonLikeError.java
new file mode 100644
index 0000000..609bd84
--- /dev/null
+++ b/src/main/java/moe/nea/pcj/json/JsonLikeError.java
@@ -0,0 +1,4 @@
+package moe.nea.pcj.json;
+
+public interface JsonLikeError {
+}
diff --git a/src/main/java/moe/nea/pcj/json/JsonLikeOperations.java b/src/main/java/moe/nea/pcj/json/JsonLikeOperations.java
new file mode 100644
index 0000000..7bb0162
--- /dev/null
+++ b/src/main/java/moe/nea/pcj/json/JsonLikeOperations.java
@@ -0,0 +1,32 @@
+package moe.nea.pcj.json;
+
+import moe.nea.pcj.Operation;
+import moe.nea.pcj.Result;
+import moe.nea.pcj.Unit;
+
+public interface JsonLikeOperations<Format> extends Operation<Format> {
+
+ Format createNull(Unit value);
+
+ Result<Unit, ? extends JsonLikeError> getNull(Format element);
+
+ Format createNumeric(Number value);
+
+ Result<Number, ? extends JsonLikeError> getNumeric(Format element);
+
+ Format createString(String value);
+
+ Result<String, ? extends JsonLikeError> getString(Format element);
+
+ Format createBoolean(boolean value);
+
+ Result<Boolean, ? extends JsonLikeError> getBoolean(Format format);
+
+ RecordBuilder<Format> createObject();
+
+ Result<? extends RecordView<Format>, ? extends JsonLikeError> getObject(Format format);
+
+ ListBuilder<Format, Format> createList();
+
+ Result<? extends ListView<Format>, ? extends JsonLikeError> getList(Format format);
+}
diff --git a/src/main/java/moe/nea/pcj/json/ListBuilder.java b/src/main/java/moe/nea/pcj/json/ListBuilder.java
new file mode 100644
index 0000000..c6e9220
--- /dev/null
+++ b/src/main/java/moe/nea/pcj/json/ListBuilder.java
@@ -0,0 +1,9 @@
+package moe.nea.pcj.json;
+
+public interface ListBuilder<Format, ElementFormat> extends ListView<ElementFormat> {
+ Format complete();
+
+ void add(ElementFormat value);
+
+ void set(int index, ElementFormat value);
+}
diff --git a/src/main/java/moe/nea/pcj/json/ListView.java b/src/main/java/moe/nea/pcj/json/ListView.java
new file mode 100644
index 0000000..dcc6e37
--- /dev/null
+++ b/src/main/java/moe/nea/pcj/json/ListView.java
@@ -0,0 +1,14 @@
+package moe.nea.pcj.json;
+
+import java.util.Optional;
+
+public interface ListView<Format> {
+ int length();
+
+ default Optional<Format> getSafe(int index) {
+ if (index < 0 || index >= length()) return Optional.empty();
+ return Optional.of(getUnsafe(index));
+ }
+
+ Format getUnsafe(int index);
+}
diff --git a/src/main/java/moe/nea/pcj/json/MapCodec.java b/src/main/java/moe/nea/pcj/json/MapCodec.java
new file mode 100644
index 0000000..468e4cf
--- /dev/null
+++ b/src/main/java/moe/nea/pcj/json/MapCodec.java
@@ -0,0 +1,17 @@
+package moe.nea.pcj.json;
+
+import moe.nea.pcj.Result;
+
+import java.util.function.Function;
+
+public interface MapCodec<T, Format> {
+ Result<T, JsonLikeError> decode(
+ RecordView<Format> record,
+ JsonLikeOperations<Format> ops);
+
+ Result<RecordBuilder<Format>, JsonLikeError> encode(T value, JsonLikeOperations<Format> ops);
+
+ default <O> RecordCodec<O, T, Format> withGetter(Function<O, T> getter) {
+ return new RecordCodec<>(this, getter);
+ }
+}
diff --git a/src/main/java/moe/nea/pcj/json/MissingKey.java b/src/main/java/moe/nea/pcj/json/MissingKey.java
new file mode 100644
index 0000000..3dad05c
--- /dev/null
+++ b/src/main/java/moe/nea/pcj/json/MissingKey.java
@@ -0,0 +1,4 @@
+package moe.nea.pcj.json;
+
+public record MissingKey(String missingKey) implements JsonLikeError {
+}
diff --git a/src/main/java/moe/nea/pcj/json/RecordBuilder.java b/src/main/java/moe/nea/pcj/json/RecordBuilder.java
new file mode 100644
index 0000000..c610f27
--- /dev/null
+++ b/src/main/java/moe/nea/pcj/json/RecordBuilder.java
@@ -0,0 +1,12 @@
+package moe.nea.pcj.json;
+
+import moe.nea.pcj.Result;
+import moe.nea.pcj.Unit;
+
+public interface RecordBuilder<ElementFormat> extends RecordView<ElementFormat> {
+ Result<Unit, JsonLikeError> add(String key, ElementFormat value);
+
+ Result<RecordBuilder<ElementFormat>, JsonLikeError> mergeWith(RecordBuilder<ElementFormat> other);
+
+ ElementFormat complete();
+}
diff --git a/src/main/java/moe/nea/pcj/json/RecordCodec.java b/src/main/java/moe/nea/pcj/json/RecordCodec.java
new file mode 100644
index 0000000..7ba8815
--- /dev/null
+++ b/src/main/java/moe/nea/pcj/json/RecordCodec.java
@@ -0,0 +1,59 @@
+package moe.nea.pcj.json;
+
+import moe.nea.pcj.Codec;
+import moe.nea.pcj.Result;
+import moe.nea.pcj.Tuple;
+
+import java.util.function.Function;
+import java.util.stream.Stream;
+
+public record RecordCodec<O, T, Format>(
+ MapCodec<T, Format> codec,
+ Function<O, T> getter
+) {
+
+ private Result<RecordBuilder<Format>, JsonLikeError> enc(O data, JsonLikeOperations<Format> ops) {
+ return codec().encode(getter().apply(data), ops);
+ }
+
+ private Result<T, JsonLikeError> dec(RecordView<Format> data, JsonLikeOperations<Format> ops) {
+ return codec().decode(data, ops);
+ }
+
+ private static <Format> Result<RecordBuilder<Format>, JsonLikeError> merge(Result<RecordBuilder<Format>, JsonLikeError> left, Result<RecordBuilder<Format>, JsonLikeError> right) {
+ return left.flatMap(l -> right.flatMap(l::mergeWith));
+ }
+
+ abstract static class RecordCompleteCodec<O, Format> implements JsonCodec<O, Format> {
+ @Override
+ public Result<O, JsonLikeError> decode(Format format, JsonLikeOperations<Format> ops) {
+ return Result.<RecordView<Format>, JsonLikeError>cast(ops.getObject(format))
+ .flatMap(record -> (decode(record, ops)));
+ }
+
+ protected abstract Result<O, JsonLikeError> decode(RecordView<Format> record, JsonLikeOperations<Format> ops);
+ }
+
+ public static <T1, T2, O, Format> JsonCodec<O, Format> join(
+ RecordCodec<O, T1, Format> arg1,
+ RecordCodec<O, T2, Format> arg2,
+ Tuple.Func2<O, T1, T2> joiner
+ ) {
+ return new RecordCompleteCodec<>() {
+
+ @Override
+ public Result<Format, JsonLikeError> encode(O data, JsonLikeOperations<Format> ops) {
+ return Stream.of(arg1.enc(data, ops), arg2.enc(data, ops))
+ .reduce(Result.ok(ops.createObject()), RecordCodec::merge)
+ .map(RecordBuilder::complete);
+ }
+
+ @Override
+ public Result<O, JsonLikeError> decode(RecordView<Format> format, JsonLikeOperations<Format> ops) {
+ return Tuple.Tuple2.collect(new Tuple.Tuple2<>(arg1, arg2)
+ .map(it -> it.dec(format, ops), it -> it.dec(format, ops)))
+ .map(it -> it.applyTo(joiner));
+ }
+ };
+ }
+}
diff --git a/src/main/java/moe/nea/pcj/json/RecordView.java b/src/main/java/moe/nea/pcj/json/RecordView.java
new file mode 100644
index 0000000..968a936
--- /dev/null
+++ b/src/main/java/moe/nea/pcj/json/RecordView.java
@@ -0,0 +1,10 @@
+package moe.nea.pcj.json;
+
+import java.util.Collection;
+import java.util.Optional;
+
+public interface RecordView<Format> {
+ Collection<String> getKeys();
+
+ Optional<Format> get(String key);
+}
diff --git a/src/main/java/moe/nea/pcj/json/UnexpectedJsonElement.java b/src/main/java/moe/nea/pcj/json/UnexpectedJsonElement.java
new file mode 100644
index 0000000..7bee7c1
--- /dev/null
+++ b/src/main/java/moe/nea/pcj/json/UnexpectedJsonElement.java
@@ -0,0 +1,7 @@
+package moe.nea.pcj.json;
+
+public record UnexpectedJsonElement(
+ String expectedType,
+ Object actualJsonObject
+) implements JsonLikeError {
+}
diff --git a/src/main/java/moe/nea/pcj/json/package-info.java b/src/main/java/moe/nea/pcj/json/package-info.java
new file mode 100644
index 0000000..a5aace0
--- /dev/null
+++ b/src/main/java/moe/nea/pcj/json/package-info.java
@@ -0,0 +1,4 @@
+@NullMarked
+package moe.nea.pcj.json;
+
+import org.jspecify.annotations.NullMarked; \ No newline at end of file