diff --git a/src/main/scala/gs/predicate/v0/api/And.scala b/src/main/scala/gs/predicate/v0/api/And.scala index 8151b63..ff20cd9 100644 --- a/src/main/scala/gs/predicate/v0/api/And.scala +++ b/src/main/scala/gs/predicate/v0/api/And.scala @@ -34,7 +34,7 @@ object And: def empty[F[_]: Applicative]: Predicate[F] = False[F] - def apply[F[_]: Applicative, A](ps: Predicate[F]*): Predicate[F] = + def apply[F[_]: Applicative](ps: Predicate[F]*): Predicate[F] = ps.toList match case Nil => False[F] case p :: Nil => p diff --git a/src/main/scala/gs/predicate/v0/api/Or.scala b/src/main/scala/gs/predicate/v0/api/Or.scala index 59cfd74..fadab1c 100644 --- a/src/main/scala/gs/predicate/v0/api/Or.scala +++ b/src/main/scala/gs/predicate/v0/api/Or.scala @@ -33,7 +33,7 @@ object Or: def empty[F[_]: Applicative]: Predicate[F] = False[F] - def apply[F[_]: Applicative, A](ps: Predicate[F]*): Predicate[F] = + def apply[F[_]: Applicative](ps: Predicate[F]*): Predicate[F] = ps.toList match case Nil => False[F] case p :: Nil => p diff --git a/src/main/scala/gs/predicate/v0/serde/json/codecs.scala b/src/main/scala/gs/predicate/v0/serde/json/codecs.scala index d6afb9b..ee1aae9 100644 --- a/src/main/scala/gs/predicate/v0/serde/json/codecs.scala +++ b/src/main/scala/gs/predicate/v0/serde/json/codecs.scala @@ -6,6 +6,7 @@ import gs.predicate.v0.api.False import gs.predicate.v0.api.Or import gs.predicate.v0.api.Predicate import gs.predicate.v0.api.True +import gs.predicate.v0.json.JsonProvider import gs.predicate.v0.kv.KeyExists import gs.predicate.v0.kv.KeyValueProvider import gs.predicate.v0.kv.ValueContains @@ -62,29 +63,26 @@ given predicateEncoder[F[_]]: Encoder[Predicate[F]] = } /** Given some JSON cursor, decode a logical [[And]] predicate. - * - * TODO: Recursive depth limitations. * * @param cursor * The cursor that points to some JSON value. * @return * The [[And]] predicate or a decoding failure. */ -def decodeAnd[F[_]: Applicative: KeyValueProvider](cursor: HCursor) - : Either[DecodingFailure, And[F]] = +def decodeAnd[F[_]: Applicative: KeyValueProvider: JsonProvider]( + cursor: HCursor +): Either[DecodingFailure, And[F]] = for ps <- cursor.downField(JsonKeys.predicates).as[List[Predicate[F]]] yield new And(ps) /** Given some JSON cursor, decode a logical [[Or]] predicate. - * - * TODO: Recursive depth limitations. * * @param cursor * The cursor that points to some JSON value. * @return * The [[Or]] predicate or a decoding failure. */ -def decodeOr[F[_]: Applicative: KeyValueProvider](cursor: HCursor) +def decodeOr[F[_]: Applicative: KeyValueProvider: JsonProvider](cursor: HCursor) : Either[DecodingFailure, Or[F]] = for ps <- cursor.downField(JsonKeys.predicates).as[List[Predicate[F]]] yield new Or(ps) @@ -96,8 +94,9 @@ def decodeOr[F[_]: Applicative: KeyValueProvider](cursor: HCursor) * @return * The decoded [[Predicate]] or some decoding failure. */ -def decodePredicate[F[_]: Applicative: KeyValueProvider](cursor: HCursor) - : Either[DecodingFailure, Predicate[F]] = +def decodePredicate[F[_]: Applicative: KeyValueProvider: JsonProvider]( + cursor: HCursor +): Either[DecodingFailure, Predicate[F]] = for predicateType <- cursor.downField(JsonKeys.predicateType).as[String] predicate <- predicateType match @@ -122,6 +121,6 @@ def decodePredicate[F[_]: Applicative: KeyValueProvider](cursor: HCursor) /** @return * Generic decoder for any [[Predicate]]. */ -given predicateDecoder[F[_]: Applicative: KeyValueProvider] +given predicateDecoder[F[_]: Applicative: KeyValueProvider: JsonProvider] : Decoder[Predicate[F]] = Decoder.instance[Predicate[F]](cursor => decodePredicate[F](cursor)) diff --git a/src/test/scala/gs/predicate/v0/api/AndTests.scala b/src/test/scala/gs/predicate/v0/api/AndTests.scala index b985fc8..0dfbf68 100644 --- a/src/test/scala/gs/predicate/v0/api/AndTests.scala +++ b/src/test/scala/gs/predicate/v0/api/AndTests.scala @@ -31,8 +31,8 @@ class AndTests extends IOSuite: iotest("should return false for an empty list") { val and = And.empty[IO] - val and2 = And[IO, Any]() - val and3 = And[IO, Any]() + val and2 = And[IO]() + val and3 = And[IO]() for result <- and.eval() diff --git a/src/test/scala/gs/predicate/v0/api/OrTests.scala b/src/test/scala/gs/predicate/v0/api/OrTests.scala index d12b9dc..ab98546 100644 --- a/src/test/scala/gs/predicate/v0/api/OrTests.scala +++ b/src/test/scala/gs/predicate/v0/api/OrTests.scala @@ -31,8 +31,8 @@ class OrTests extends IOSuite: iotest("should return false for an empty list") { val or = Or.empty[IO] - val or2 = Or[IO, Any]() - val or3 = Or[IO, Any]() + val or2 = Or[IO]() + val or3 = Or[IO]() for result <- or.eval() diff --git a/src/test/scala/gs/predicate/v0/serde/json/ApiCodecTests.scala b/src/test/scala/gs/predicate/v0/serde/json/ApiCodecTests.scala new file mode 100644 index 0000000..11da434 --- /dev/null +++ b/src/test/scala/gs/predicate/v0/serde/json/ApiCodecTests.scala @@ -0,0 +1,84 @@ +package gs.predicate.v0.serde.json + +import cats.effect.IO +import gs.predicate.v0.api.And +import gs.predicate.v0.api.False +import gs.predicate.v0.api.Or +import gs.predicate.v0.api.Predicate +import gs.predicate.v0.api.True +import gs.predicate.v0.json.JsonProvider +import gs.predicate.v0.kv.KeyValueProvider +import io.circe.Decoder +import io.circe.Encoder +import io.circe.Json +import munit.FunSuite + +class ApiCodecTests extends FunSuite: + + test("should serialize and deserialize a True predicate") { + val p = True[IO] + val encoded = Encoder[True[IO]].apply(p) + val decoded = Decoder[True[IO]].decodeJson(encoded) + assertEquals( + encoded, + Json.obj( + (JsonKeys.predicateType, Json.fromString(True.PredicateType)) + ) + ) + assertEquals(decoded.isRight, true) + } + + test("should serialize and deserialize a False predicate") { + val p = False[IO] + val encoded = Encoder[False[IO]].apply(p) + val decoded = Decoder[False[IO]].decodeJson(encoded) + assertEquals( + encoded, + Json.obj( + (JsonKeys.predicateType, Json.fromString(False.PredicateType)) + ) + ) + assertEquals(decoded.isRight, true) + } + + test("should serialize and deserialize an And predicate") { + given JsonProvider[IO] = JsonProvider.noop[IO] + given KeyValueProvider[IO] = KeyValueProvider.noop[IO] + + val t = True[IO] + val f = False[IO] + val trueJson = Encoder[True[IO]].apply(t) + val falseJson = Encoder[False[IO]].apply(f) + val p = And[IO](t, t, f) + val encoded = Encoder[And[IO]].apply(p.asInstanceOf[And[IO]]) + val decoded = Decoder[Predicate[IO]].decodeJson(encoded) + assertEquals( + encoded, + Json.obj( + (JsonKeys.predicateType, Json.fromString(And.PredicateType)), + (JsonKeys.predicates, Json.arr(trueJson, trueJson, falseJson)) + ) + ) + assertEquals(decoded.isRight, true) + } + + test("should serialize and deserialize an Or predicate") { + given JsonProvider[IO] = JsonProvider.noop[IO] + given KeyValueProvider[IO] = KeyValueProvider.noop[IO] + + val t = True[IO] + val f = False[IO] + val trueJson = Encoder[True[IO]].apply(t) + val falseJson = Encoder[False[IO]].apply(f) + val p = Or[IO](t, t, f) + val encoded = Encoder[Or[IO]].apply(p.asInstanceOf[Or[IO]]) + val decoded = Decoder[Predicate[IO]].decodeJson(encoded) + assertEquals( + encoded, + Json.obj( + (JsonKeys.predicateType, Json.fromString(Or.PredicateType)), + (JsonKeys.predicates, Json.arr(trueJson, trueJson, falseJson)) + ) + ) + assertEquals(decoded.isRight, true) + }