package gs.predicate.v0.kv import cats.Functor import cats.syntax.all.* import gs.predicate.v0.api.Messages import gs.predicate.v0.api.Predicate import gs.predicate.v0.serde.json.JsonKeys import io.circe.Decoder import io.circe.DecodingFailure import io.circe.Encoder import io.circe.Json /** Predicate that matches if some [[KeyValueProvider]] contains the given key, * and the string value associated with that key contains some other string. * * @param key * The key that should exist. * @param containedValue * The substring that must be contained in the value. */ final class ValueContains[F[_]: Functor: KeyValueProvider]( val key: String, val containedValue: String ) extends Predicate[F]: /** @inheritDocs */ override def predicateType: String = ValueContains.PredicateType /** @inheritDocs */ override def eval(): F[Predicate.Result] = KeyValueProvider[F].get(key).map { case Some(value) => Predicate.Result(value.contains(containedValue)) case _ => Predicate.Result.missed() } object ValueContains: final val PredicateType: String = "kv-string-contains" def apply[F[_]: Functor: KeyValueProvider]( key: String, containedValue: String ): ValueContains[F] = new ValueContains[F](key, containedValue) given valueContainsEncoder[F[_]]: Encoder[ValueContains[F]] = Encoder.instance[ValueContains[F]] { p => Json.obj( (JsonKeys.predicateType, Json.fromString(PredicateType)), (JsonKeys.key, Json.fromString(p.key)), (JsonKeys.value, Json.fromString(p.containedValue)) ) } given valueContainsDecoder[F[_]: Functor: KeyValueProvider] : Decoder[ValueContains[F]] = Decoder.instance[ValueContains[F]] { cursor => cursor.downField(JsonKeys.predicateType).as[String].flatMap { case PredicateType => for key <- cursor.downField(JsonKeys.key).as[String] value <- cursor.downField(JsonKeys.value).as[String] yield new ValueContains(key, value) case candidate => Left( DecodingFailure( Messages.invalidPredicateType(candidate, PredicateType), Nil ) ) } } end ValueContains