summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLinnea Gräf <nea@nea.moe>2024-11-14 02:59:11 +0100
committerLinnea Gräf <nea@nea.moe>2024-11-14 02:59:11 +0100
commit5eea18ea497d4bc11466b2bd875582ba26f188f5 (patch)
tree26391b4e347a97eafed954a52cd81b14e10a8314
parent05937d237c2fdf793e21b36523d06902c5a0a725 (diff)
downloadprofunctor-codecs-java-5eea18ea497d4bc11466b2bd875582ba26f188f5.tar.gz
profunctor-codecs-java-5eea18ea497d4bc11466b2bd875582ba26f188f5.tar.bz2
profunctor-codecs-java-5eea18ea497d4bc11466b2bd875582ba26f188f5.zip
Add lists
-rw-r--r--gson/src/main/java/moe/nea/jcp/gson/GsonRecordBuilder.java5
-rw-r--r--gson/src/test/java/moe/nea/jcp/gson/test/TestBasic.java21
-rw-r--r--src/main/java/moe/nea/pcj/Result.java1
-rw-r--r--src/main/java/moe/nea/pcj/json/AtIndex.java4
-rw-r--r--src/main/java/moe/nea/pcj/json/JsonCodec.java40
-rw-r--r--src/main/java/moe/nea/pcj/json/MapCodec.java4
6 files changed, 72 insertions, 3 deletions
diff --git a/gson/src/main/java/moe/nea/jcp/gson/GsonRecordBuilder.java b/gson/src/main/java/moe/nea/jcp/gson/GsonRecordBuilder.java
index ded1953..43498ac 100644
--- a/gson/src/main/java/moe/nea/jcp/gson/GsonRecordBuilder.java
+++ b/gson/src/main/java/moe/nea/jcp/gson/GsonRecordBuilder.java
@@ -23,9 +23,8 @@ public class GsonRecordBuilder extends GsonRecordView implements RecordBuilder<J
return Result.cast(Stream.of(this.complete(), other.complete())
.flatMap(it -> ((JsonObject) it).entrySet().stream())
.map(it -> next.add(it.getKey(), it.getValue()))
- .reduce((left, right) -> left.appendErrors(right.errors()))
- .map(it -> it.map(unit -> next))
- .orElse(Result.ok(next)));
+ .reduce(Result.ok(Unit.INSTANCE), (left, right) -> left.appendErrors(right.errors()))
+ .map(unit -> next));
}
@Override
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 58727f2..861656e 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
@@ -1,5 +1,6 @@
package moe.nea.jcp.gson.test;
+import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonNull;
import com.google.gson.JsonObject;
@@ -10,6 +11,7 @@ import moe.nea.pcj.Codec;
import moe.nea.pcj.Decode;
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.JsonLikeError;
import moe.nea.pcj.json.JsonLikeOperations;
@@ -21,6 +23,7 @@ import org.junit.jupiter.api.Test;
import java.util.Arrays;
import java.util.HashSet;
+import java.util.List;
import java.util.stream.Collectors;
public class TestBasic {
@@ -55,6 +58,14 @@ public class TestBasic {
throw new IllegalArgumentException("Cannot convert " + arg + " to json object");
}
+ static JsonArray mkJsonArray(Object... args) {
+ JsonArray array = new JsonArray();
+ for (Object arg : args) {
+ array.add(mkPrim(arg));
+ }
+ return array;
+ }
+
static JsonObject mkJsonObject(Object... args) {
JsonObject obj = new JsonObject();
for (int i = 0; i < args.length; i += 2) {
@@ -99,6 +110,7 @@ public class TestBasic {
assertFail(decode(codec, mkJsonObject("foo", "fooValue", "bar", "test")),
new AtField("bar", new UnexpectedJsonElement("number", mkPrim("test"))));
}
+
@Test
void testDuplicateKeys() {
var codec = RecordJoiners.join(
@@ -110,5 +122,14 @@ public class TestBasic {
assertFail(codec.encode(new TestObject("", 0), GsonOperations.INSTANCE),
new DuplicateJsonKey("foo"));
}
+
+ @Test
+ void testList() {
+ var codec = codecs.STRING.fieldOf("hello").codec().listOf();
+ assertSuccess(decode(codec, mkJsonArray(mkJsonObject("hello", "foo"), mkJsonObject("hello", "bar"))),
+ List.of("foo", "bar"));
+ assertFail(decode(codec, mkJsonArray("foo", mkJsonObject("hello", "bar"))),
+ new AtIndex(0, new UnexpectedJsonElement("object", mkPrim("foo"))));
+ }
}
diff --git a/src/main/java/moe/nea/pcj/Result.java b/src/main/java/moe/nea/pcj/Result.java
index 22c94f9..af5398e 100644
--- a/src/main/java/moe/nea/pcj/Result.java
+++ b/src/main/java/moe/nea/pcj/Result.java
@@ -7,6 +7,7 @@ import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
+import java.util.function.Consumer;
import java.util.function.Function;
public sealed interface Result<Good, Bad> permits Result.Ok, Result.Fail {
diff --git a/src/main/java/moe/nea/pcj/json/AtIndex.java b/src/main/java/moe/nea/pcj/json/AtIndex.java
new file mode 100644
index 0000000..ec01112
--- /dev/null
+++ b/src/main/java/moe/nea/pcj/json/AtIndex.java
@@ -0,0 +1,4 @@
+package moe.nea.pcj.json;
+
+public record AtIndex(int index, JsonLikeError error) implements JsonLikeError {
+}
diff --git a/src/main/java/moe/nea/pcj/json/JsonCodec.java b/src/main/java/moe/nea/pcj/json/JsonCodec.java
index 65f4a5e..6195bee 100644
--- a/src/main/java/moe/nea/pcj/json/JsonCodec.java
+++ b/src/main/java/moe/nea/pcj/json/JsonCodec.java
@@ -2,11 +2,51 @@ package moe.nea.pcj.json;
import moe.nea.pcj.Codec;
import moe.nea.pcj.Result;
+import moe.nea.pcj.Unit;
+
+import java.util.ArrayList;
+import java.util.List;
public interface JsonCodec<T, Format> extends Codec<
T, Format, JsonLikeOperations<Format>,
JsonLikeError, JsonLikeError> {
+ default JsonCodec<List<T>, Format> listOf() {
+ return new JsonCodec<>() {
+ @Override
+ public Result<Format, JsonLikeError> encode(List<T> data, JsonLikeOperations<Format> ops) {
+ var list = ops.createList();
+ var erros = new ArrayList<JsonLikeError>();
+ for (int i = 0; i < data.size(); i++) {
+ var datum = data.get(i);
+ final var index = i;
+ var result = JsonCodec.this.encode(datum, ops)
+ .mapError(it -> new AtIndex(index, it));
+ erros.addAll(result.errors());
+ result.valueOrPartial().ifPresent(list::add);
+ }
+ return Result.<Format, JsonLikeError>ok(list.complete()).appendErrors(erros);
+ }
+
+ @Override
+ public Result<List<T>, JsonLikeError> decode(Format format, JsonLikeOperations<Format> ops) {
+ var view = Result.<ListView<Format>, JsonLikeError>cast(ops.getList(format));
+ return view.flatMap(elements -> {
+ var acc = new ArrayList<T>(elements.length());
+ var errors = new ArrayList<JsonLikeError>();
+ for (int i = 0; i < elements.length(); i++) {
+ final var index = i;
+ var result = JsonCodec.this.decode(elements.getUnsafe(i), ops)
+ .mapError(it -> new AtIndex(index, it));
+ errors.addAll(result.errors());
+ result.valueOrPartial().ifPresent(acc::add);
+ }
+ return Result.<List<T>, JsonLikeError>ok(acc).appendErrors(errors);
+ });
+ }
+ };
+ }
+
default MapCodec<T, Format> fieldOf(String key) {
return new MapCodec<>() {
@Override
diff --git a/src/main/java/moe/nea/pcj/json/MapCodec.java b/src/main/java/moe/nea/pcj/json/MapCodec.java
index 468e4cf..4b76150 100644
--- a/src/main/java/moe/nea/pcj/json/MapCodec.java
+++ b/src/main/java/moe/nea/pcj/json/MapCodec.java
@@ -11,6 +11,10 @@ public interface MapCodec<T, Format> {
Result<RecordBuilder<Format>, JsonLikeError> encode(T value, JsonLikeOperations<Format> ops);
+ default JsonCodec<T, Format> codec() {
+ return RecordJoiners.join(withGetter(it -> it), it -> it);
+ }
+
default <O> RecordCodec<O, T, Format> withGetter(Function<O, T> getter) {
return new RecordCodec<>(this, getter);
}