Working on the key/value module
This commit is contained in:
parent
af109358b5
commit
ed83e30518
10 changed files with 280 additions and 2 deletions
23
build.sbt
23
build.sbt
|
|
@ -49,7 +49,8 @@ lazy val `gs-predicate` = project
|
||||||
.in(file("."))
|
.in(file("."))
|
||||||
.aggregate(
|
.aggregate(
|
||||||
`test-support`,
|
`test-support`,
|
||||||
api
|
api,
|
||||||
|
keyValue
|
||||||
)
|
)
|
||||||
.settings(noPublishSettings)
|
.settings(noPublishSettings)
|
||||||
.settings(name := s"${gsProjectName.value}-v${semVerMajor.value}")
|
.settings(name := s"${gsProjectName.value}-v${semVerMajor.value}")
|
||||||
|
|
@ -71,7 +72,7 @@ lazy val `test-support` = project
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
/** Core API - the only dependency needed to write tests.
|
/** Core API - Defines fundamental predicates.
|
||||||
*/
|
*/
|
||||||
lazy val api = project
|
lazy val api = project
|
||||||
.in(file("modules/api"))
|
.in(file("modules/api"))
|
||||||
|
|
@ -81,6 +82,24 @@ lazy val api = project
|
||||||
.settings(
|
.settings(
|
||||||
name := s"${gsProjectName.value}-api-v${semVerMajor.value}"
|
name := s"${gsProjectName.value}-api-v${semVerMajor.value}"
|
||||||
)
|
)
|
||||||
|
.settings(
|
||||||
|
libraryDependencies ++= Seq(
|
||||||
|
Deps.Cats.Core,
|
||||||
|
Deps.Gs.Uuid
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
/** Key-Value - Defines predicates that can match on string keys and values.
|
||||||
|
*/
|
||||||
|
lazy val keyValue = project
|
||||||
|
.in(file("modules/kv"))
|
||||||
|
.dependsOn(`test-support` % "test->test")
|
||||||
|
.dependsOn(api)
|
||||||
|
.settings(sharedSettings)
|
||||||
|
.settings(testSettings)
|
||||||
|
.settings(
|
||||||
|
name := s"${gsProjectName.value}-kv-v${semVerMajor.value}"
|
||||||
|
)
|
||||||
.settings(
|
.settings(
|
||||||
libraryDependencies ++= Seq(
|
libraryDependencies ++= Seq(
|
||||||
Deps.Cats.Core,
|
Deps.Cats.Core,
|
||||||
|
|
|
||||||
23
modules/kv/src/main/scala/gs/predicate/v0/kv/KeyExists.scala
Normal file
23
modules/kv/src/main/scala/gs/predicate/v0/kv/KeyExists.scala
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
package gs.predicate.v0.kv
|
||||||
|
|
||||||
|
import cats.effect.Sync
|
||||||
|
import cats.syntax.all.*
|
||||||
|
import gs.predicate.v0.api.Predicate
|
||||||
|
import gs.uuid.v0.UUID
|
||||||
|
|
||||||
|
/** Predicate that matches if some [[KeyValueProvider]] contains the given key.
|
||||||
|
*
|
||||||
|
* @param id
|
||||||
|
* The unique identifier of this [[Predicate]].
|
||||||
|
* @param key
|
||||||
|
* The key that should exist.
|
||||||
|
*/
|
||||||
|
final class KeyExists[F[_]: Sync, K, V](
|
||||||
|
val id: UUID,
|
||||||
|
val key: K
|
||||||
|
) extends Predicate[F, KeyValueProvider[F, K, V]]:
|
||||||
|
|
||||||
|
/** @inheritDocs
|
||||||
|
*/
|
||||||
|
override def eval(input: KeyValueProvider[F, K, V]): F[Predicate.Result] =
|
||||||
|
input.exists(key).map(Predicate.Result.apply)
|
||||||
|
|
@ -0,0 +1,44 @@
|
||||||
|
package gs.predicate.v0.kv
|
||||||
|
|
||||||
|
import cats.Applicative
|
||||||
|
|
||||||
|
/** Interface for anything that can fetch values for stored keys.
|
||||||
|
*/
|
||||||
|
trait KeyValueProvider[F[_], -K, V]:
|
||||||
|
/** Determine if some key exists.
|
||||||
|
*
|
||||||
|
* @param key
|
||||||
|
* The key to check.
|
||||||
|
* @return
|
||||||
|
* True if the key exists, false otherwise.
|
||||||
|
*/
|
||||||
|
def exists(key: K): F[Boolean]
|
||||||
|
|
||||||
|
/** Get the value associated with some key.
|
||||||
|
*
|
||||||
|
* @param key
|
||||||
|
* The key to fetch.
|
||||||
|
* @return
|
||||||
|
* The value stored for the key, or `None` if no such value exists.
|
||||||
|
*/
|
||||||
|
def get(key: K): F[Option[V]]
|
||||||
|
|
||||||
|
object KeyValueProvider:
|
||||||
|
|
||||||
|
/** @return
|
||||||
|
* New instance of the no-op [[KeyValueProvider]] implementation.
|
||||||
|
*/
|
||||||
|
def noop[F[_]: Applicative]: KeyValueProvider[F, Any, Any] = new Noop[F]
|
||||||
|
|
||||||
|
/** No-op implementation that never contains data.
|
||||||
|
*/
|
||||||
|
final class Noop[F[_]: Applicative] extends KeyValueProvider[F, Any, Any]:
|
||||||
|
/** @inheritDocs
|
||||||
|
*/
|
||||||
|
override def exists(key: Any): F[Boolean] = Applicative[F].pure(false)
|
||||||
|
|
||||||
|
/** @inheritDocs
|
||||||
|
*/
|
||||||
|
override def get(key: Any): F[Option[Any]] = Applicative[F].pure(None)
|
||||||
|
|
||||||
|
end KeyValueProvider
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
package gs.predicate.v0.kv
|
||||||
|
|
||||||
|
/** Type alias for a [[KeyValueProvider]] that associates string values with
|
||||||
|
* string keys.
|
||||||
|
*/
|
||||||
|
type KeyValueStringProvider[F[_]] = KeyValueProvider[F, String, String]
|
||||||
|
|
@ -0,0 +1,24 @@
|
||||||
|
package gs.predicate.v0.kv
|
||||||
|
|
||||||
|
import cats.effect.Sync
|
||||||
|
import cats.effect.std.MapRef
|
||||||
|
import cats.syntax.all.*
|
||||||
|
|
||||||
|
/** Provides keys and values, given some in-memory map.
|
||||||
|
*
|
||||||
|
* @param map
|
||||||
|
* The underlying map.
|
||||||
|
*/
|
||||||
|
final class MemoryMapStringProvider[F[_]: Sync](
|
||||||
|
private val map: MapRef[F, String, Option[String]]
|
||||||
|
) extends KeyValueStringProvider[F]:
|
||||||
|
|
||||||
|
/** @inheritDocs
|
||||||
|
*/
|
||||||
|
override def exists(key: String): F[Boolean] =
|
||||||
|
map.apply(key).get.map(_.isDefined)
|
||||||
|
|
||||||
|
/** @inheritDocs
|
||||||
|
*/
|
||||||
|
override def get(key: String): F[Option[String]] =
|
||||||
|
map.apply(key).get
|
||||||
|
|
@ -0,0 +1,32 @@
|
||||||
|
package gs.predicate.v0.kv
|
||||||
|
|
||||||
|
import cats.effect.Sync
|
||||||
|
import cats.syntax.all.*
|
||||||
|
import gs.predicate.v0.api.Predicate
|
||||||
|
import gs.uuid.v0.UUID
|
||||||
|
|
||||||
|
/** Predicate that matches if some (string-valued) [[KeyValueProvider]] contains
|
||||||
|
* the given key, and the string value associated with that key contains some
|
||||||
|
* other string.
|
||||||
|
*
|
||||||
|
* @param id
|
||||||
|
* The unique identifier of this [[Predicate]].
|
||||||
|
* @param key
|
||||||
|
* The key that should exist.
|
||||||
|
* @param containedValue
|
||||||
|
* The substring that must be contained in the value.
|
||||||
|
*/
|
||||||
|
final class StringContains[F[_]: Sync, K](
|
||||||
|
val id: UUID,
|
||||||
|
val key: K,
|
||||||
|
val containedValue: String
|
||||||
|
) extends Predicate[F, KeyValueProvider[F, K, String]]:
|
||||||
|
|
||||||
|
/** @inheritDocs
|
||||||
|
*/
|
||||||
|
override def eval(input: KeyValueProvider[F, K, String])
|
||||||
|
: F[Predicate.Result] =
|
||||||
|
input.get(key).map {
|
||||||
|
case Some(value) => Predicate.Result(value.contains(containedValue))
|
||||||
|
case _ => Predicate.Result.missed()
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,32 @@
|
||||||
|
package gs.predicate.v0.kv
|
||||||
|
|
||||||
|
import cats.effect.Sync
|
||||||
|
import cats.syntax.all.*
|
||||||
|
import gs.predicate.v0.api.Predicate
|
||||||
|
import gs.uuid.v0.UUID
|
||||||
|
|
||||||
|
/** Predicate that matches if some (string-valued) [[KeyValueProvider]] contains
|
||||||
|
* the given key, and the string value associated with that key ends with some
|
||||||
|
* other string.
|
||||||
|
*
|
||||||
|
* @param id
|
||||||
|
* The unique identifier of this [[Predicate]].
|
||||||
|
* @param key
|
||||||
|
* The key that should exist.
|
||||||
|
* @param suffix
|
||||||
|
* The substring that must be the suffix of the value.
|
||||||
|
*/
|
||||||
|
final class StringEndsWith[F[_]: Sync, K](
|
||||||
|
val id: UUID,
|
||||||
|
val key: K,
|
||||||
|
val suffix: String
|
||||||
|
) extends Predicate[F, KeyValueProvider[F, K, String]]:
|
||||||
|
|
||||||
|
/** @inheritDocs
|
||||||
|
*/
|
||||||
|
override def eval(input: KeyValueProvider[F, K, String])
|
||||||
|
: F[Predicate.Result] =
|
||||||
|
input.get(key).map {
|
||||||
|
case Some(value) => Predicate.Result(value.endsWith(suffix))
|
||||||
|
case _ => Predicate.Result.missed()
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,32 @@
|
||||||
|
package gs.predicate.v0.kv
|
||||||
|
|
||||||
|
import cats.effect.Sync
|
||||||
|
import cats.syntax.all.*
|
||||||
|
import gs.predicate.v0.api.Predicate
|
||||||
|
import gs.uuid.v0.UUID
|
||||||
|
|
||||||
|
/** Predicate that matches if some (string-valued) [[KeyValueProvider]] contains
|
||||||
|
* the given key, and the string value associated with that key starts with
|
||||||
|
* some other string.
|
||||||
|
*
|
||||||
|
* @param id
|
||||||
|
* The unique identifier of this [[Predicate]].
|
||||||
|
* @param key
|
||||||
|
* The key that should exist.
|
||||||
|
* @param prefix
|
||||||
|
* The substring that must be the prefix of the value.
|
||||||
|
*/
|
||||||
|
final class StringStartsWith[F[_]: Sync, K](
|
||||||
|
val id: UUID,
|
||||||
|
val key: K,
|
||||||
|
val prefix: String
|
||||||
|
) extends Predicate[F, KeyValueProvider[F, K, String]]:
|
||||||
|
|
||||||
|
/** @inheritDocs
|
||||||
|
*/
|
||||||
|
override def eval(input: KeyValueProvider[F, K, String])
|
||||||
|
: F[Predicate.Result] =
|
||||||
|
input.get(key).map {
|
||||||
|
case Some(value) => Predicate.Result(value.startsWith(prefix))
|
||||||
|
case _ => Predicate.Result.missed()
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,33 @@
|
||||||
|
package gs.predicate.v0.kv
|
||||||
|
|
||||||
|
import cats.effect.Sync
|
||||||
|
import cats.syntax.all.*
|
||||||
|
import gs.predicate.v0.api.Predicate
|
||||||
|
import gs.uuid.v0.UUID
|
||||||
|
|
||||||
|
/** Predicate that matches if some [[KeyValueProvider]] contains the given key,
|
||||||
|
* and the value associated with that key equals the given value.
|
||||||
|
*
|
||||||
|
* @param id
|
||||||
|
* The unique identifier of this [[Predicate]].
|
||||||
|
* @param key
|
||||||
|
* The key that should exist.
|
||||||
|
* @param value
|
||||||
|
* The value that must be associated with the key.
|
||||||
|
*/
|
||||||
|
final class ValueEquals[F[_]: Sync, K, V](
|
||||||
|
val id: UUID,
|
||||||
|
val key: K,
|
||||||
|
val expectedValue: V
|
||||||
|
)(
|
||||||
|
using
|
||||||
|
CanEqual[V, V]
|
||||||
|
) extends Predicate[F, KeyValueProvider[F, K, V]]:
|
||||||
|
|
||||||
|
/** @inheritDocs
|
||||||
|
*/
|
||||||
|
override def eval(input: KeyValueProvider[F, K, V]): F[Predicate.Result] =
|
||||||
|
input.get(key).map {
|
||||||
|
case Some(value) => Predicate.Result(value == expectedValue)
|
||||||
|
case _ => Predicate.Result.missed()
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,33 @@
|
||||||
|
package gs.predicate.v0.kv
|
||||||
|
|
||||||
|
import cats.effect.Sync
|
||||||
|
import cats.syntax.all.*
|
||||||
|
import gs.predicate.v0.api.Predicate
|
||||||
|
import gs.uuid.v0.UUID
|
||||||
|
|
||||||
|
/** Predicate that matches if some [[KeyValueProvider]] contains the given key,
|
||||||
|
* and the value associated with that key is not equal to the given value.
|
||||||
|
*
|
||||||
|
* @param id
|
||||||
|
* The unique identifier of this [[Predicate]].
|
||||||
|
* @param key
|
||||||
|
* The key that should exist.
|
||||||
|
* @param value
|
||||||
|
* The value that must not be associated with the key.
|
||||||
|
*/
|
||||||
|
final class ValueNotEquals[F[_]: Sync, K, V](
|
||||||
|
val id: UUID,
|
||||||
|
val key: K,
|
||||||
|
val expectedValue: V
|
||||||
|
)(
|
||||||
|
using
|
||||||
|
CanEqual[V, V]
|
||||||
|
) extends Predicate[F, KeyValueProvider[F, K, V]]:
|
||||||
|
|
||||||
|
/** @inheritDocs
|
||||||
|
*/
|
||||||
|
override def eval(input: KeyValueProvider[F, K, V]): F[Predicate.Result] =
|
||||||
|
input.get(key).map {
|
||||||
|
case Some(value) => Predicate.Result(value != expectedValue)
|
||||||
|
case _ => Predicate.Result.missed()
|
||||||
|
}
|
||||||
Loading…
Add table
Reference in a new issue