From ed8eb00566a5e3b7f2d5564d7977a4b3ec8fe5b3 Mon Sep 17 00:00:00 2001 From: Linnea Gräf Date: Thu, 14 Nov 2024 01:35:24 +0100 Subject: wip --- src/main/java/moe/nea/pcj/json/AtField.java | 4 ++ src/main/java/moe/nea/pcj/json/BasicCodecs.java | 51 +++++++++++++++++++ .../java/moe/nea/pcj/json/DuplicateJsonKey.java | 4 ++ src/main/java/moe/nea/pcj/json/JsonCodec.java | 31 ++++++++++++ src/main/java/moe/nea/pcj/json/JsonLikeError.java | 4 ++ .../java/moe/nea/pcj/json/JsonLikeOperations.java | 32 ++++++++++++ src/main/java/moe/nea/pcj/json/ListBuilder.java | 9 ++++ src/main/java/moe/nea/pcj/json/ListView.java | 14 +++++ src/main/java/moe/nea/pcj/json/MapCodec.java | 17 +++++++ src/main/java/moe/nea/pcj/json/MissingKey.java | 4 ++ src/main/java/moe/nea/pcj/json/RecordBuilder.java | 12 +++++ src/main/java/moe/nea/pcj/json/RecordCodec.java | 59 ++++++++++++++++++++++ src/main/java/moe/nea/pcj/json/RecordView.java | 10 ++++ .../moe/nea/pcj/json/UnexpectedJsonElement.java | 7 +++ src/main/java/moe/nea/pcj/json/package-info.java | 4 ++ 15 files changed, 262 insertions(+) create mode 100644 src/main/java/moe/nea/pcj/json/AtField.java create mode 100644 src/main/java/moe/nea/pcj/json/BasicCodecs.java create mode 100644 src/main/java/moe/nea/pcj/json/DuplicateJsonKey.java create mode 100644 src/main/java/moe/nea/pcj/json/JsonCodec.java create mode 100644 src/main/java/moe/nea/pcj/json/JsonLikeError.java create mode 100644 src/main/java/moe/nea/pcj/json/JsonLikeOperations.java create mode 100644 src/main/java/moe/nea/pcj/json/ListBuilder.java create mode 100644 src/main/java/moe/nea/pcj/json/ListView.java create mode 100644 src/main/java/moe/nea/pcj/json/MapCodec.java create mode 100644 src/main/java/moe/nea/pcj/json/MissingKey.java create mode 100644 src/main/java/moe/nea/pcj/json/RecordBuilder.java create mode 100644 src/main/java/moe/nea/pcj/json/RecordCodec.java create mode 100644 src/main/java/moe/nea/pcj/json/RecordView.java create mode 100644 src/main/java/moe/nea/pcj/json/UnexpectedJsonElement.java create mode 100644 src/main/java/moe/nea/pcj/json/package-info.java (limited to 'src/main/java/moe/nea/pcj/json') 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 { + protected BasicCodecs() {} + + public static BasicCodecs create() { + return new BasicCodecs<>(); + } + + public final JsonCodec STRING = new JsonCodec<>() { + @Override + public Result decode(Format format, JsonLikeOperations ops) { + return ops.getString(format); + } + + @Override + public Result encode(String data, JsonLikeOperations ops) { + return Result.ok(ops.createString(data)); + } + }; + + public final JsonCodec FLOAT = new JsonCodec() { + @Override + public Result decode(Format format, JsonLikeOperations ops) { + return ops.getNumeric(format).map(Number::floatValue); + } + + @Override + public Result encode(Float data, JsonLikeOperations ops) { + return Result.ok(ops.createNumeric(data)); + } + }; + + public final JsonCodec INTEGER = new JsonCodec<>() { + @Override + public Result decode(Format format, JsonLikeOperations ops) { + return ops.getNumeric(format).map(Number::intValue); // TODO: filter for valid ints + } + + @Override + public Result encode(Integer data, JsonLikeOperations ops) { + return Result.ok(ops.createNumeric(data)); + } + }; + + public JsonCodec 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 extends Codec< + T, Format, JsonLikeOperations, + JsonLikeError, JsonLikeError> { + + default MapCodec fieldOf(String key) { + return new MapCodec<>() { + @Override + public Result decode(RecordView record, JsonLikeOperations ops) { + return record.get(key) + .map(element -> Result.cast( + JsonCodec.this.decode(element, ops) + .mapError(it -> new AtField(key, it)))) + .orElseGet(() -> Result.fail(new MissingKey(key))); + } + + @Override + public Result, JsonLikeError> encode(T value, JsonLikeOperations ops) { + var record = ops.createObject(); + return Result.cast(JsonCodec.this.encode(value, ops)) + .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 extends Operation { + + Format createNull(Unit value); + + Result getNull(Format element); + + Format createNumeric(Number value); + + Result getNumeric(Format element); + + Format createString(String value); + + Result getString(Format element); + + Format createBoolean(boolean value); + + Result getBoolean(Format format); + + RecordBuilder createObject(); + + Result, ? extends JsonLikeError> getObject(Format format); + + ListBuilder createList(); + + Result, ? 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 extends ListView { + 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 { + int length(); + + default Optional 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 { + Result decode( + RecordView record, + JsonLikeOperations ops); + + Result, JsonLikeError> encode(T value, JsonLikeOperations ops); + + default RecordCodec withGetter(Function 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 extends RecordView { + Result add(String key, ElementFormat value); + + Result, JsonLikeError> mergeWith(RecordBuilder 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( + MapCodec codec, + Function getter +) { + + private Result, JsonLikeError> enc(O data, JsonLikeOperations ops) { + return codec().encode(getter().apply(data), ops); + } + + private Result dec(RecordView data, JsonLikeOperations ops) { + return codec().decode(data, ops); + } + + private static Result, JsonLikeError> merge(Result, JsonLikeError> left, Result, JsonLikeError> right) { + return left.flatMap(l -> right.flatMap(l::mergeWith)); + } + + abstract static class RecordCompleteCodec implements JsonCodec { + @Override + public Result decode(Format format, JsonLikeOperations ops) { + return Result., JsonLikeError>cast(ops.getObject(format)) + .flatMap(record -> (decode(record, ops))); + } + + protected abstract Result decode(RecordView record, JsonLikeOperations ops); + } + + public static JsonCodec join( + RecordCodec arg1, + RecordCodec arg2, + Tuple.Func2 joiner + ) { + return new RecordCompleteCodec<>() { + + @Override + public Result encode(O data, JsonLikeOperations 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 decode(RecordView format, JsonLikeOperations 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 { + Collection getKeys(); + + Optional 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 -- cgit