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 value associated with that key is contained in the set of given * values. * * @param key * The key that should exist. * @param values * The list of values, such that one value should match the input. */ final class ValueIn[F[_]: Functor: KeyValueProvider]( val key: String, val values: Set[String] ) extends Predicate[F]: /** @inheritDocs */ override def predicateType: String = ValueIn.PredicateType /** @inheritDocs */ override def eval(): F[Predicate.Result] = KeyValueProvider[F].get(key).map { case Some(value) => Predicate.Result(values.contains(value)) case _ => Predicate.Result.missed() } object ValueIn: final val PredicateType: String = "kv-value-in" def apply[F[_]: Functor: KeyValueProvider]( key: String, values: Set[String] ): ValueIn[F] = new ValueIn[F](key, values) given valueInEncoder[F[_]]: Encoder[ValueIn[F]] = Encoder.instance[ValueIn[F]] { p => Json.obj( (JsonKeys.predicateType, Json.fromString(PredicateType)), (JsonKeys.key, Json.fromString(p.key)), (JsonKeys.values, Json.fromValues(p.values.map(Json.fromString))) ) } given valueInDecoder[F[_]: Functor: KeyValueProvider]: Decoder[ValueIn[F]] = Decoder.instance[ValueIn[F]] { cursor => cursor.downField(JsonKeys.predicateType).as[String].flatMap { case PredicateType => for key <- cursor.downField(JsonKeys.key).as[String] values <- cursor.downField(JsonKeys.value).as[Set[String]] yield new ValueIn(key, values) case candidate => Left( DecodingFailure( Messages.invalidPredicateType(candidate, PredicateType), Nil ) ) } } end ValueIn