gs-predicate/src/main/scala/gs/predicate/v0/serde/json/codecs.scala

366 lines
15 KiB
Scala

package gs.predicate.v0.serde.json
import gs.predicate.v0.api.And
import gs.predicate.v0.api.False
import gs.predicate.v0.api.Messages
import gs.predicate.v0.api.Or
import gs.predicate.v0.api.Predicate
import gs.predicate.v0.api.True
import gs.predicate.v0.json.JsonComparison
import gs.predicate.v0.json.JsonComparisonPredicate
import gs.predicate.v0.string.StringComparison
import gs.predicate.v0.string.StringComparisonPredicate
import io.circe.*
import io.circe.syntax.*
/** Given some [[And]] predicate, encode it as a JSON blob.
*
* @param and
* The [[And]] predicate.
* @return
* The JSON blob representing the [[And]].
*/
def encodeAnd(and: And): Json =
Json.obj(
(JsonKeys.predicateType, Json.fromString(And.PredicateType)),
(JsonKeys.predicates, and.ps.asJson)
)
def encodeJsonComparisonAnd(and: JsonComparison.And): Json =
Json.obj(
(JsonKeys.name, Json.fromString(JsonComparison.And.Name)),
(JsonKeys.comparisons, and.cs.asJson)
)
def encodeStringComparisonAnd(and: StringComparison.And): Json =
Json.obj(
(JsonKeys.name, Json.fromString(StringComparison.And.Name)),
(JsonKeys.comparisons, and.cs.asJson)
)
/** Given some [[Or]] predicate, encode it as a JSON blob.
*
* @param and
* The [[Or]] predicate.
* @return
* The JSON blob representing the [[Or]].
*/
def encodeOr(and: Or): Json =
Json.obj(
(JsonKeys.predicateType, Json.fromString(Or.PredicateType)),
(JsonKeys.predicates, and.ps.asJson)
)
def encodeJsonComparisonOr(or: JsonComparison.Or): Json =
Json.obj(
(JsonKeys.name, Json.fromString(JsonComparison.Or.Name)),
(JsonKeys.comparisons, or.cs.asJson)
)
def encodeStringComparisonOr(or: StringComparison.Or): Json =
Json.obj(
(JsonKeys.name, Json.fromString(StringComparison.Or.Name)),
(JsonKeys.comparisons, or.cs.asJson)
)
/** @return
* Generic encoder for any [[Predicate]].
*/
given predicateEncoder: Encoder[Predicate] =
Encoder.instance {
case p: True.type => Encoder[True.type].apply(p)
case p: False.type => Encoder[False.type].apply(p)
case p: And => encodeAnd(p)
case p: Or => encodeOr(p)
case p: StringComparisonPredicate =>
Encoder[StringComparisonPredicate].apply(p)
case p: JsonComparisonPredicate => Encoder[JsonComparisonPredicate].apply(p)
case p =>
throw new IllegalArgumentException(
s"Unsupported predicate type: '${p.predicateType}'"
)
}
given jsonComparisonEncoder: Encoder[JsonComparison] =
Encoder.instance[JsonComparison] {
case jc: JsonComparison.And => encodeJsonComparisonAnd(jc)
case jc: JsonComparison.Or => encodeJsonComparisonOr(jc)
case jc: JsonComparison.True.type =>
Encoder[JsonComparison.True.type].apply(jc)
case jc: JsonComparison.False.type =>
Encoder[JsonComparison.False.type].apply(jc)
case jc: JsonComparison.Eq => Encoder[JsonComparison.Eq].apply(jc)
case jc: JsonComparison.Neq => Encoder[JsonComparison.Neq].apply(jc)
case jc: JsonComparison.StringContains =>
Encoder[JsonComparison.StringContains].apply(jc)
case jc: JsonComparison.StringPrefix =>
Encoder[JsonComparison.StringPrefix].apply(jc)
case jc: JsonComparison.StringSuffix =>
Encoder[JsonComparison.StringSuffix].apply(jc)
case jc: JsonComparison.IntLessThan =>
Encoder[JsonComparison.IntLessThan].apply(jc)
case jc: JsonComparison.IntLessThanOrEqualTo =>
Encoder[JsonComparison.IntLessThanOrEqualTo].apply(jc)
case jc: JsonComparison.IntGreaterThan =>
Encoder[JsonComparison.IntGreaterThan].apply(jc)
case jc: JsonComparison.IntGreaterThanOrEqualTo =>
Encoder[JsonComparison.IntGreaterThanOrEqualTo].apply(jc)
case jc: JsonComparison.IntBetweenInclusive =>
Encoder[JsonComparison.IntBetweenInclusive].apply(jc)
case jc: JsonComparison.IntBetweenExclusive =>
Encoder[JsonComparison.IntBetweenExclusive].apply(jc)
case jc: JsonComparison.DateEq => Encoder[JsonComparison.DateEq].apply(jc)
case jc: JsonComparison.DateBefore =>
Encoder[JsonComparison.DateBefore].apply(jc)
case jc: JsonComparison.DateAfter =>
Encoder[JsonComparison.DateAfter].apply(jc)
case jc: JsonComparison.DateBetweenInclusive =>
Encoder[JsonComparison.DateBetweenInclusive].apply(jc)
case jc: JsonComparison.DateBetweenExclusive =>
Encoder[JsonComparison.DateBetweenExclusive].apply(jc)
case jc =>
throw new IllegalArgumentException(
s"Unsupported JSON comparison: '${jc.name}'"
)
}
given stringComparisonEncoder: Encoder[StringComparison] =
Encoder.instance[StringComparison] {
case jc: StringComparison.And => encodeStringComparisonAnd(jc)
case jc: StringComparison.Or => encodeStringComparisonOr(jc)
case jc: StringComparison.True.type =>
Encoder[StringComparison.True.type].apply(jc)
case jc: StringComparison.False.type =>
Encoder[StringComparison.False.type].apply(jc)
case jc: StringComparison.Eq => Encoder[StringComparison.Eq].apply(jc)
case jc: StringComparison.Neq => Encoder[StringComparison.Neq].apply(jc)
case jc: StringComparison.StringContains =>
Encoder[StringComparison.StringContains].apply(jc)
case jc: StringComparison.StringPrefix =>
Encoder[StringComparison.StringPrefix].apply(jc)
case jc: StringComparison.StringSuffix =>
Encoder[StringComparison.StringSuffix].apply(jc)
case jc: StringComparison.IntLessThan =>
Encoder[StringComparison.IntLessThan].apply(jc)
case jc: StringComparison.IntLessThanOrEqualTo =>
Encoder[StringComparison.IntLessThanOrEqualTo].apply(jc)
case jc: StringComparison.IntGreaterThan =>
Encoder[StringComparison.IntGreaterThan].apply(jc)
case jc: StringComparison.IntGreaterThanOrEqualTo =>
Encoder[StringComparison.IntGreaterThanOrEqualTo].apply(jc)
case jc: StringComparison.IntBetweenInclusive =>
Encoder[StringComparison.IntBetweenInclusive].apply(jc)
case jc: StringComparison.IntBetweenExclusive =>
Encoder[StringComparison.IntBetweenExclusive].apply(jc)
case jc: StringComparison.DateEq =>
Encoder[StringComparison.DateEq].apply(jc)
case jc: StringComparison.DateBefore =>
Encoder[StringComparison.DateBefore].apply(jc)
case jc: StringComparison.DateAfter =>
Encoder[StringComparison.DateAfter].apply(jc)
case jc: StringComparison.DateBetweenInclusive =>
Encoder[StringComparison.DateBetweenInclusive].apply(jc)
case jc: StringComparison.DateBetweenExclusive =>
Encoder[StringComparison.DateBetweenExclusive].apply(jc)
case jc =>
throw new IllegalArgumentException(
s"Unsupported string comparison: '${jc.name}'"
)
}
/** Given some JSON cursor, decode a logical [[And]] predicate.
*
* @param cursor
* The cursor that points to some JSON value.
* @return
* The [[And]] predicate or a decoding failure.
*/
def decodeAnd(
cursor: HCursor
): Either[DecodingFailure, And] =
for ps <- cursor.downField(JsonKeys.predicates).as[List[Predicate]]
yield new And(ps)
def decodeJsonComparisonAnd(
cursor: HCursor
): Either[DecodingFailure, JsonComparison.And] =
for cs <- cursor.downField(JsonKeys.comparisons).as[List[JsonComparison]]
yield new JsonComparison.And(cs)
def decodeStringComparisonAnd(
cursor: HCursor
): Either[DecodingFailure, StringComparison.And] =
for cs <- cursor.downField(JsonKeys.comparisons).as[List[StringComparison]]
yield new StringComparison.And(cs)
/** Given some JSON cursor, decode a logical [[Or]] predicate.
*
* @param cursor
* The cursor that points to some JSON value.
* @return
* The [[Or]] predicate or a decoding failure.
*/
def decodeOr(cursor: HCursor): Either[DecodingFailure, Or] =
for ps <- cursor.downField(JsonKeys.predicates).as[List[Predicate]]
yield new Or(ps)
def decodeJsonComparisonOr(
cursor: HCursor
): Either[DecodingFailure, JsonComparison.Or] =
for cs <- cursor.downField(JsonKeys.comparisons).as[List[JsonComparison]]
yield new JsonComparison.Or(cs)
def decodeStringComparisonOr(
cursor: HCursor
): Either[DecodingFailure, StringComparison.Or] =
for cs <- cursor.downField(JsonKeys.comparisons).as[List[StringComparison]]
yield new StringComparison.Or(cs)
/** Given some JSON cursor, decode any known [[Predicate]].
*
* @param cursor
* The cursor that points to some JSON value.
* @return
* The decoded [[Predicate]] or some decoding failure.
*/
def decodePredicate(
cursor: HCursor
): Either[DecodingFailure, Predicate] =
for
predicateType <- cursor.downField(JsonKeys.predicateType).as[String]
predicate <- predicateType match
case True.PredicateType => Right(True)
case False.PredicateType => Right(False)
case And.PredicateType => decodeAnd(cursor)
case Or.PredicateType => decodeOr(cursor)
case StringComparisonPredicate.PredicateType =>
Decoder[StringComparisonPredicate].apply(cursor)
case JsonComparisonPredicate.PredicateType =>
Decoder[JsonComparisonPredicate].apply(cursor)
case _ =>
Left(
DecodingFailure(
Messages.unrecognizedPredicateType(predicateType),
Nil
)
)
yield predicate
/** @return
* Generic decoder for any [[Predicate]].
*/
given predicateDecoder: Decoder[Predicate] =
Decoder.instance[Predicate](cursor => decodePredicate(cursor))
/** Given some JSON cursor, decode any known JSON comparison.
*
* @param cursor
* The cursor.
* @return
* The decoded [[JsonComparison]] or a decoding failure.
*/
def decodeJsonComparison(cursor: HCursor)
: Either[DecodingFailure, JsonComparison] =
for
name <- cursor.downField(JsonKeys.name).as[String]
comparison <- name match
case JsonComparison.And.Name => decodeJsonComparisonAnd(cursor)
case JsonComparison.Or.Name => decodeJsonComparisonOr(cursor)
case JsonComparison.True.Name =>
Decoder[JsonComparison.True.type].apply(cursor)
case JsonComparison.False.Name =>
Decoder[JsonComparison.False.type].apply(cursor)
case JsonComparison.Eq.Name => Decoder[JsonComparison.Eq].apply(cursor)
case JsonComparison.Neq.Name => Decoder[JsonComparison.Neq].apply(cursor)
case JsonComparison.StringContains.Name =>
Decoder[JsonComparison.StringContains].apply(cursor)
case JsonComparison.StringPrefix.Name =>
Decoder[JsonComparison.StringPrefix].apply(cursor)
case JsonComparison.StringSuffix.Name =>
Decoder[JsonComparison.StringSuffix].apply(cursor)
case JsonComparison.IntLessThan.Name =>
Decoder[JsonComparison.IntLessThan].apply(cursor)
case JsonComparison.IntLessThanOrEqualTo.Name =>
Decoder[JsonComparison.IntLessThanOrEqualTo].apply(cursor)
case JsonComparison.IntGreaterThan.Name =>
Decoder[JsonComparison.IntGreaterThan].apply(cursor)
case JsonComparison.IntGreaterThanOrEqualTo.Name =>
Decoder[JsonComparison.IntGreaterThanOrEqualTo].apply(cursor)
case JsonComparison.IntBetweenInclusive.Name =>
Decoder[JsonComparison.IntBetweenInclusive].apply(cursor)
case JsonComparison.IntBetweenExclusive.Name =>
Decoder[JsonComparison.IntBetweenExclusive].apply(cursor)
case JsonComparison.DateEq.Name =>
Decoder[JsonComparison.DateEq].apply(cursor)
case JsonComparison.DateBefore.Name =>
Decoder[JsonComparison.DateBefore].apply(cursor)
case JsonComparison.DateAfter.Name =>
Decoder[JsonComparison.DateAfter].apply(cursor)
case JsonComparison.DateBetweenInclusive.Name =>
Decoder[JsonComparison.DateBetweenInclusive].apply(cursor)
case JsonComparison.DateBetweenExclusive.Name =>
Decoder[JsonComparison.DateBetweenExclusive].apply(cursor)
case _ =>
Left(
DecodingFailure(Messages.unrecognizedJsonComparison(name), Nil)
)
yield comparison
/** Generic decoder for any [[JsonComparison]].
*/
given jsonComparisonDecoder: Decoder[JsonComparison] =
Decoder.instance[JsonComparison](decodeJsonComparison)
def decodeStringComparison(cursor: HCursor)
: Either[DecodingFailure, StringComparison] =
for
name <- cursor.downField(JsonKeys.name).as[String]
comparison <- name match
case StringComparison.And.Name => decodeStringComparisonAnd(cursor)
case StringComparison.Or.Name => decodeStringComparisonOr(cursor)
case StringComparison.True.Name =>
Decoder[StringComparison.True.type].apply(cursor)
case StringComparison.False.Name =>
Decoder[StringComparison.False.type].apply(cursor)
case StringComparison.Eq.Name =>
Decoder[StringComparison.Eq].apply(cursor)
case StringComparison.Neq.Name =>
Decoder[StringComparison.Neq].apply(cursor)
case StringComparison.StringContains.Name =>
Decoder[StringComparison.StringContains].apply(cursor)
case StringComparison.StringPrefix.Name =>
Decoder[StringComparison.StringPrefix].apply(cursor)
case StringComparison.StringSuffix.Name =>
Decoder[StringComparison.StringSuffix].apply(cursor)
case StringComparison.IntLessThan.Name =>
Decoder[StringComparison.IntLessThan].apply(cursor)
case StringComparison.IntLessThanOrEqualTo.Name =>
Decoder[StringComparison.IntLessThanOrEqualTo].apply(cursor)
case StringComparison.IntGreaterThan.Name =>
Decoder[StringComparison.IntGreaterThan].apply(cursor)
case StringComparison.IntGreaterThanOrEqualTo.Name =>
Decoder[StringComparison.IntGreaterThanOrEqualTo].apply(cursor)
case StringComparison.IntBetweenInclusive.Name =>
Decoder[StringComparison.IntBetweenInclusive].apply(cursor)
case StringComparison.IntBetweenExclusive.Name =>
Decoder[StringComparison.IntBetweenExclusive].apply(cursor)
case StringComparison.DateEq.Name =>
Decoder[StringComparison.DateEq].apply(cursor)
case StringComparison.DateBefore.Name =>
Decoder[StringComparison.DateBefore].apply(cursor)
case StringComparison.DateAfter.Name =>
Decoder[StringComparison.DateAfter].apply(cursor)
case StringComparison.DateBetweenInclusive.Name =>
Decoder[StringComparison.DateBetweenInclusive].apply(cursor)
case StringComparison.DateBetweenExclusive.Name =>
Decoder[StringComparison.DateBetweenExclusive].apply(cursor)
case _ =>
Left(
DecodingFailure(Messages.unrecognizedStringComparison(name), Nil)
)
yield comparison
/** Generic decoder for any [[StringComparison]].
*/
given stringComparisonDecoder: Decoder[StringComparison] =
Decoder.instance[StringComparison](decodeStringComparison)