diff options
author | Linnea Gräf <nea@nea.moe> | 2024-11-14 01:35:24 +0100 |
---|---|---|
committer | Linnea Gräf <nea@nea.moe> | 2024-11-14 01:35:24 +0100 |
commit | ed8eb00566a5e3b7f2d5564d7977a4b3ec8fe5b3 (patch) | |
tree | 9f8418ce1f27634a5a5f37a07f57c45379584d87 /src/main/java/moe/nea/pcj/json | |
parent | 94e30f7ffbafcdd188d09d8c7bf7e4794b650018 (diff) | |
download | profunctor-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.java | 4 | ||||
-rw-r--r-- | src/main/java/moe/nea/pcj/json/BasicCodecs.java | 51 | ||||
-rw-r--r-- | src/main/java/moe/nea/pcj/json/DuplicateJsonKey.java | 4 | ||||
-rw-r--r-- | src/main/java/moe/nea/pcj/json/JsonCodec.java | 31 | ||||
-rw-r--r-- | src/main/java/moe/nea/pcj/json/JsonLikeError.java | 4 | ||||
-rw-r--r-- | src/main/java/moe/nea/pcj/json/JsonLikeOperations.java | 32 | ||||
-rw-r--r-- | src/main/java/moe/nea/pcj/json/ListBuilder.java | 9 | ||||
-rw-r--r-- | src/main/java/moe/nea/pcj/json/ListView.java | 14 | ||||
-rw-r--r-- | src/main/java/moe/nea/pcj/json/MapCodec.java | 17 | ||||
-rw-r--r-- | src/main/java/moe/nea/pcj/json/MissingKey.java | 4 | ||||
-rw-r--r-- | src/main/java/moe/nea/pcj/json/RecordBuilder.java | 12 | ||||
-rw-r--r-- | src/main/java/moe/nea/pcj/json/RecordCodec.java | 59 | ||||
-rw-r--r-- | src/main/java/moe/nea/pcj/json/RecordView.java | 10 | ||||
-rw-r--r-- | src/main/java/moe/nea/pcj/json/UnexpectedJsonElement.java | 7 | ||||
-rw-r--r-- | src/main/java/moe/nea/pcj/json/package-info.java | 4 |
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 |