From 63cb7a0e19e7afd06637dddb848d0254cbff5795 Mon Sep 17 00:00:00 2001 From: Linnea Gräf Date: Thu, 14 Nov 2024 21:50:10 +0100 Subject: Better errors for dispatch codecs --- .../test/java/moe/nea/jcp/gson/test/TestBasic.java | 58 +++++++++++++++++----- .../java/moe/nea/pcj/json/DuringKeyExtraction.java | 4 ++ src/main/java/moe/nea/pcj/json/InSubType.java | 4 ++ src/main/java/moe/nea/pcj/json/MapCodec.java | 24 ++++++--- src/main/java/moe/nea/pcj/json/UnknownSubtype.java | 10 ++++ 5 files changed, 79 insertions(+), 21 deletions(-) create mode 100644 src/main/java/moe/nea/pcj/json/DuringKeyExtraction.java create mode 100644 src/main/java/moe/nea/pcj/json/InSubType.java create mode 100644 src/main/java/moe/nea/pcj/json/UnknownSubtype.java diff --git a/gson/src/test/java/moe/nea/jcp/gson/test/TestBasic.java b/gson/src/test/java/moe/nea/jcp/gson/test/TestBasic.java index 628518c..6e70665 100644 --- a/gson/src/test/java/moe/nea/jcp/gson/test/TestBasic.java +++ b/gson/src/test/java/moe/nea/jcp/gson/test/TestBasic.java @@ -13,12 +13,15 @@ import moe.nea.pcj.Result; import moe.nea.pcj.json.AtField; import moe.nea.pcj.json.AtIndex; import moe.nea.pcj.json.DuplicateJsonKey; +import moe.nea.pcj.json.DuringKeyExtraction; +import moe.nea.pcj.json.InSubType; import moe.nea.pcj.json.JsonLikeError; import moe.nea.pcj.json.JsonLikeOperations; import moe.nea.pcj.json.MissingKey; import moe.nea.pcj.json.NamedObject; import moe.nea.pcj.json.RecordJoiners; import moe.nea.pcj.json.UnexpectedJsonElement; +import moe.nea.pcj.json.UnknownSubtype; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; @@ -37,7 +40,7 @@ public class TestBasic { ); } - private void assertSuccess(Result result, T expected) { + private void assertSuccess(Result result, T expected) { for (Object error : result.errors()) { throw new AssertionError(error.toString()); } @@ -146,6 +149,10 @@ public class TestBasic { new AtIndex(0, new UnexpectedJsonElement("object", mkPrim("foo")))); } + T implicitCast(T t) { + return t; + } + @Test void testDispatched() { var testObjectCodec = RecordJoiners.join( @@ -157,18 +164,43 @@ public class TestBasic { codecs.STRING.fieldOf("test").withGetter(OtherTestObject::test), OtherTestObject::new ); - codecs.STRING.fieldOf("type") - .dispatch( - obj -> switch (obj) { - case OtherTestObject ignored -> "other"; - case TestObject ignored -> "normal"; - }, - key -> switch (key) { - case "other" -> otherObjectCodec; - case "normal" -> testObjectCodec; - default -> throw new AssertionError("Unknown thing"); - } - ); + var parentCodec = codecs.STRING.fieldOf("type") + .dispatch( + obj -> switch (obj) { + case OtherTestObject ignored -> "other"; + case TestObject ignored -> "normal"; + }, + key -> switch (key) { + case "other" -> Result.ok(otherObjectCodec); + case "normal" -> Result.ok(testObjectCodec); + default -> Result.fail(new UnknownSubtype<>(key, "other", "normal")); + } + ); + assertSuccess(decode(parentCodec.codec(), mkJsonObject( + "type", "other", + "test", "test" + )), new OtherTestObject("test")); + assertSuccess(decode(parentCodec.codec(), mkJsonObject( + "type", "normal", + "foo", "fooVal", + "bar", 10 + )), new TestObject("fooVal", 10)); + assertFail( + decode(parentCodec.codec(), mkJsonObject( + "type", "unknown", + "foo", "fooVal", + "bar", 10 + )), + new DuringKeyExtraction(new UnknownSubtype<>("unknown", "other", "normal")) + ); + assertFail( + decode(parentCodec.codec(), mkJsonObject( + "type", "other", + "foo", "fooVal", + "bar", 10 + )), + new InSubType<>("other", new MissingKey("test")) + ); } } diff --git a/src/main/java/moe/nea/pcj/json/DuringKeyExtraction.java b/src/main/java/moe/nea/pcj/json/DuringKeyExtraction.java new file mode 100644 index 0000000..859f98f --- /dev/null +++ b/src/main/java/moe/nea/pcj/json/DuringKeyExtraction.java @@ -0,0 +1,4 @@ +package moe.nea.pcj.json; + +public record DuringKeyExtraction(JsonLikeError error) implements JsonLikeError { +} diff --git a/src/main/java/moe/nea/pcj/json/InSubType.java b/src/main/java/moe/nea/pcj/json/InSubType.java new file mode 100644 index 0000000..ae62a73 --- /dev/null +++ b/src/main/java/moe/nea/pcj/json/InSubType.java @@ -0,0 +1,4 @@ +package moe.nea.pcj.json; + +public record InSubType(T typeTag, JsonLikeError error) implements JsonLikeError { +} diff --git a/src/main/java/moe/nea/pcj/json/MapCodec.java b/src/main/java/moe/nea/pcj/json/MapCodec.java index b18a639..9f343fd 100644 --- a/src/main/java/moe/nea/pcj/json/MapCodec.java +++ b/src/main/java/moe/nea/pcj/json/MapCodec.java @@ -13,24 +13,32 @@ public interface MapCodec { default MapCodec dispatch( Function keyExtractor, - Function> codecGenerator + Function, ? extends JsonLikeError>> codecGenerator ) { // TODO: the codecGenerator function is not exactly typesafe. there should be some limit on keyExtractor and codecGenerator working in tandem return new MapCodec<>() { @Override - public Result decode(RecordView record, JsonLikeOperations ops) { + public Result decode(RecordView record, JsonLikeOperations ops) { // TODO: map errors return MapCodec.this.decode(record, ops) - .map(codecGenerator::apply) - .flatMap(codec -> codec.decode(record, ops)); + .flatMap(key -> codecGenerator + .apply(key) + .mapError(DuringKeyExtraction::new) + .flatMap(codec -> codec.decode(record, ops) + .mapError(it -> new InSubType<>(key, it)))); } @Override public Result, JsonLikeError> encode(O value, JsonLikeOperations ops) { var key = keyExtractor.apply(value); - var codec = codecGenerator.apply(key); - return MapCodec.this - .encode(key, ops) - .flatMap(keyEncoded -> ((MapCodec) codec).encode(value, ops).flatMap(keyEncoded::mergeWith)); + return Result., JsonLikeError>cast( + codecGenerator.apply(key) + .mapError(DuringKeyExtraction::new)) + .flatMap(codec -> MapCodec.this + .encode(key, ops) + .flatMap(keyEncoded -> ((MapCodec) codec) + .encode(value, ops) + .mapError(it -> new InSubType<>(key, it)) + .flatMap(keyEncoded::mergeWith))); } }; } diff --git a/src/main/java/moe/nea/pcj/json/UnknownSubtype.java b/src/main/java/moe/nea/pcj/json/UnknownSubtype.java new file mode 100644 index 0000000..942a7dc --- /dev/null +++ b/src/main/java/moe/nea/pcj/json/UnknownSubtype.java @@ -0,0 +1,10 @@ +package moe.nea.pcj.json; + +import java.util.Set; + +public record UnknownSubtype(T actual, Set expectedTypes) implements JsonLikeError { + @SafeVarargs + public UnknownSubtype(T actual, T... expected) { + this(actual, Set.of(expected)); + } +} -- cgit