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 starts with some other string. * * @param key * The key that should exist. * @param prefix * The substring that must be the prefix of the value. */ final class ValueStartsWith[F[_]: Functor: KeyValueProvider]( val key: String, val prefix: String ) extends Predicate[F]: /** @inheritDocs */ override def predicateType: String = ValueStartsWith.PredicateType /** @inheritDocs */ override def eval(): F[Predicate.Result] = KeyValueProvider[F].get(key).map { case Some(value) => Predicate.Result(value.startsWith(prefix)) case _ => Predicate.Result.missed() } object ValueStartsWith: final val PredicateType: String = "kv-string-starts-with" def apply[F[_]: Functor: KeyValueProvider]( key: String, prefix: String ): ValueStartsWith[F] = new ValueStartsWith[F](key, prefix) given valueStartsWithEncoder[F[_]]: Encoder[ValueStartsWith[F]] = Encoder.instance[ValueStartsWith[F]] { p => Json.obj( (JsonKeys.predicateType, Json.fromString(PredicateType)), (JsonKeys.key, Json.fromString(p.key)), (JsonKeys.value, Json.fromString(p.prefix)) ) } given valueStartsWithDecoder[F[_]: Functor: KeyValueProvider] : Decoder[ValueStartsWith[F]] = Decoder.instance[ValueStartsWith[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 ValueStartsWith(key, value) case candidate => Left( DecodingFailure( Messages.invalidPredicateType(candidate, PredicateType), Nil ) ) } } end ValueStartsWith