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)