diff --git a/api/api/api.api b/api/api/api.api index 9aefa40..05cd392 100644 --- a/api/api/api.api +++ b/api/api/api.api @@ -216,3 +216,137 @@ public final class dev/kord/cache/api/meta/TypeStatisticsLogger { public final fun logQuery ()V } +public final class dev/kord/cache/api/observables/BasicRelation : dev/kord/cache/api/observables/Relation { + public fun ()V + public fun remove (Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public fun to (Ldev/kord/cache/api/observables/Cache;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; +} + +public abstract interface class dev/kord/cache/api/observables/Cache { + public abstract fun filter (Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public abstract fun firstOrNull (Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public abstract fun get (Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public abstract fun relatesTo (Ldev/kord/cache/api/observables/Cache;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public abstract fun remove (Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public abstract fun removeAll (Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public abstract fun removeAny (Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public abstract fun set (Ljava/lang/Object;Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; +} + +public final class dev/kord/cache/api/observables/ConcurrentCache : dev/kord/cache/api/observables/Cache { + public fun (Ldev/kord/cache/api/observables/Relation;)V + public fun filter (Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public fun firstOrNull (Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public fun get (Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public final fun getRelation ()Ldev/kord/cache/api/observables/Relation; + public fun relatesTo (Ldev/kord/cache/api/observables/Cache;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public fun remove (Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public fun removeAll (Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public fun removeAny (Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public fun set (Ljava/lang/Object;Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; +} + +public abstract interface class dev/kord/cache/api/observables/DataCache { + public abstract fun firstOrNull (Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public abstract fun get (Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public abstract fun getAll (Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public abstract fun put (Ljava/lang/Object;Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public abstract fun relatesTo (Ldev/kord/cache/api/observables/DataCache;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public abstract fun remove (Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public abstract fun removeAll (Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public abstract fun removeIf (Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; +} + +public abstract interface class dev/kord/cache/api/observables/EntryCache { + public abstract fun asMap (Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public abstract fun firstOrNull (Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public abstract fun get (Ldev/kord/cache/api/observables/Index;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public abstract fun put (Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public abstract fun relatesTo (Ldev/kord/cache/api/observables/EntryCache;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public abstract fun remove (Ldev/kord/cache/api/observables/Index;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public abstract fun removeAll (Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public abstract fun removeIf (Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; +} + +public abstract interface class dev/kord/cache/api/observables/Index : java/lang/Comparable { + public abstract fun hashCode ()I +} + +public final class dev/kord/cache/api/observables/IndexCache : dev/kord/cache/api/observables/EntryCache { + public fun (Ldev/kord/cache/api/observables/Relation;Lkotlin/jvm/functions/Function1;)V + public fun asMap (Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public fun firstOrNull (Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public fun get (Ldev/kord/cache/api/observables/Index;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public final fun getIndexFactory ()Lkotlin/jvm/functions/Function1; + public final fun getRelation ()Ldev/kord/cache/api/observables/Relation; + public fun put (Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public fun relatesTo (Ldev/kord/cache/api/observables/EntryCache;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public fun remove (Ldev/kord/cache/api/observables/Index;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public fun removeAll (Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public fun removeIf (Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; +} + +public abstract interface class dev/kord/cache/api/observables/IndexFactory { + public abstract fun createIndexFor (Ljava/lang/Object;)Ldev/kord/cache/api/observables/Index; +} + +public final class dev/kord/cache/api/observables/MemberData { + public fun (II)V + public final fun component1 ()I + public final fun component2 ()I + public final fun copy (II)Ldev/kord/cache/api/observables/MemberData; + public static synthetic fun copy$default (Ldev/kord/cache/api/observables/MemberData;IIILjava/lang/Object;)Ldev/kord/cache/api/observables/MemberData; + public fun equals (Ljava/lang/Object;)Z + public final fun getGuildId ()I + public final fun getUserId ()I + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + +public final class dev/kord/cache/api/observables/MemberDataIndexFactory : dev/kord/cache/api/observables/IndexFactory { + public static final field INSTANCE Ldev/kord/cache/api/observables/MemberDataIndexFactory; + public fun createIndexFor (Ldev/kord/cache/api/observables/MemberData;)Ldev/kord/cache/api/observables/Index; + public synthetic fun createIndexFor (Ljava/lang/Object;)Ldev/kord/cache/api/observables/Index; +} + +public abstract interface class dev/kord/cache/api/observables/Relation { + public abstract fun remove (Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public abstract fun to (Ldev/kord/cache/api/observables/Cache;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; +} + +public final class dev/kord/cache/api/observables/SnowflakeSetIndex : dev/kord/cache/api/observables/Index { + public fun (Ljava/util/Set;)V + public fun compareTo (Ldev/kord/cache/api/observables/Index;)I + public synthetic fun compareTo (Ljava/lang/Object;)I + public final fun getSnowflakes ()Ljava/util/Set; + public fun hashCode ()I +} + +public final class dev/kord/cache/api/observables/TestKt { + public static final fun main (Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public static synthetic fun main ([Ljava/lang/String;)V +} + +public abstract interface class dev/kord/cache/api/observables/TypedCache { + public abstract fun getType (Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public abstract fun putCache (Ldev/kord/cache/api/observables/EntryCache;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public abstract fun toSet (Lkotlin/coroutines/Continuation;)Ljava/lang/Object; +} + +public final class dev/kord/cache/api/observables/UserData { + public fun (I)V + public final fun component1 ()I + public final fun copy (I)Ldev/kord/cache/api/observables/UserData; + public static synthetic fun copy$default (Ldev/kord/cache/api/observables/UserData;IILjava/lang/Object;)Ldev/kord/cache/api/observables/UserData; + public fun equals (Ljava/lang/Object;)Z + public final fun getUserId ()I + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + +public final class dev/kord/cache/api/observables/UserIndexFactory : dev/kord/cache/api/observables/IndexFactory { + public static final field INSTANCE Ldev/kord/cache/api/observables/UserIndexFactory; + public fun createIndexFor (Ldev/kord/cache/api/observables/UserData;)Ldev/kord/cache/api/observables/Index; + public synthetic fun createIndexFor (Ljava/lang/Object;)Ldev/kord/cache/api/observables/Index; +} + diff --git a/api/src/commonMain/kotlin/dev/kord/cache/api/observables/BasicRelation.kt b/api/src/commonMain/kotlin/dev/kord/cache/api/observables/BasicRelation.kt new file mode 100644 index 0000000..94a6dec --- /dev/null +++ b/api/src/commonMain/kotlin/dev/kord/cache/api/observables/BasicRelation.kt @@ -0,0 +1,25 @@ +package dev.kord.cache.api.observables + +class BasicRelation : Relation { + private val relations = mutableSetOf>() + private val caches = mutableSetOf>() + + override suspend fun remove(value: T) { + relations.onEach { relatesTo -> + caches.onEach { other -> other.removeAny { friend -> relatesTo(value, friend) } } + } + } + + override suspend fun to(cache: Cache<*, R>, handler: RelationHandler) { + caches.add(cache) + relations.add(safe(handler)) + } + + private fun safe(relationHandler: RelationHandler): RelationHandler { + return obj@{ value: T, friend: Any -> + @Suppress("UNCHECKED_CAST") + val safeCast = friend as? R + safeCast != null && relationHandler(value, safeCast) + } + } +} \ No newline at end of file diff --git a/api/src/commonMain/kotlin/dev/kord/cache/api/observables/Cache.kt b/api/src/commonMain/kotlin/dev/kord/cache/api/observables/Cache.kt new file mode 100644 index 0000000..d3f2bf5 --- /dev/null +++ b/api/src/commonMain/kotlin/dev/kord/cache/api/observables/Cache.kt @@ -0,0 +1,59 @@ +package dev.kord.cache.api.observables + +import kotlinx.coroutines.flow.Flow + +/** + * A cache for a map of entries where each [Value] is associated with a unique [Key]. + * */ +interface Cache { + + /** + * Returns the value associated with the given [key] in the cache, or null if no such entry exists. + */ + suspend fun get(key: Key): Value? + + /** + * Removes the entry associated with the given [key] from the cache, if it exists. + */ + suspend fun remove(key: Key) + + /** + * Filters [Value]s by the given [predicate] + * Returns a lazy [Flow] of [Value]s + */ + suspend fun filter(predicate: (Value) -> Boolean): Sequence + + /** + * Sets the [value] into the cache associated with a [key]. + * @param key The key to associate the [value] with. + * @param value The value to be cached. + */ + suspend fun set(key: Key, value: Value) + + /** + * Returns the first value that satisfies the given [predicate] function, or null if none is found. + * @param predicate A function that takes a [Value] and returns a boolean. + * @return The first value that satisfies the [predicate] function, or null if none is found. + */ + public suspend fun firstOrNull(predicate: (Value) -> Boolean): Value? + + /** + * Removes any entry in the cache which matches the given [predicate]. + * @param predicate The predicate to match each [Value] against. + */ + public suspend fun removeAny(predicate: (Value) -> Boolean) + + /** + * Removes all entries in the cache. + */ + public suspend fun removeAll() + + /** + * Adds an observer cache to this cache. Whenever a value is removed from this cache, the + * observer cache will also remove any values that are related to it. + * @param other The observer cache. + * @param handler A [RelationHandler] that specifies how to remove related values from the observer cache. + */ + public suspend fun relatesTo(other: Cache<*, R>, handler: RelationHandler) + +} diff --git a/api/src/commonMain/kotlin/dev/kord/cache/api/observables/ConcurrentCache.kt b/api/src/commonMain/kotlin/dev/kord/cache/api/observables/ConcurrentCache.kt new file mode 100644 index 0000000..cf22a5f --- /dev/null +++ b/api/src/commonMain/kotlin/dev/kord/cache/api/observables/ConcurrentCache.kt @@ -0,0 +1,85 @@ +package dev.kord.cache.api.observables + +import co.touchlab.stately.collections.ConcurrentMutableMap + +/** + * An implementation of the [Cache] with [ConcurrentMutableMap]. + * This implementation is thread-safe. + * + * @param relation The relation between this cache and other caches, used to remove related entries. + */ +public class ConcurrentCache( + public val relation: Relation, +) : Cache { + + private val source: ConcurrentMutableMap = ConcurrentMutableMap() + + /** + * Gets the value associated with the given [key]. + * + * @return The value associated with the given [key], or `null` if not found. + */ + override suspend fun get(key: Key): Value? { + return source[key] + } + + /** + * Gets the first value that matches the [predicate] function. + * + * @return The first value that matches the [predicate] function, or `null` if not found. + */ + override suspend fun firstOrNull(predicate: (Value) -> Boolean): Value? { + return source.values.firstOrNull(predicate) + } + + /** + * removes all values that match the [predicate] function. + * + * @param predicate The function used to determine which values to remove. + */ + override suspend fun removeAny(predicate: (Value) -> Boolean) { + source.asSequence() + .filter { (_, value) -> predicate(value) } + .forEach { (key, _) -> remove(key) } + } + + override suspend fun filter(predicate: (Value) -> Boolean): Sequence = + source.asSequence() + .filter { (_, value) -> predicate(value) } + .map { it.value } + + + /** + * removes the value associated with the given [key]. + * + * @return The value associated with the given [key], or `null` if not found. + */ + override suspend fun remove(key: Key) { + val value = source[key] ?: return + relation.remove(value) + source.remove(key) + } + + override suspend fun set(key: Key, value: Value) { + source[key] = value + } + + + /** + * removes all entries from this cache. + */ + override suspend fun removeAll() { + val iterator = source.iterator() + while (iterator.hasNext()) { + val (_, value) = iterator.next() + relation.remove(value) + iterator.remove() + } + } + + + override suspend fun relatesTo(other: Cache<*, R>, handler: RelationHandler) { + relation.to(other, handler) + } + +} \ No newline at end of file diff --git a/api/src/commonMain/kotlin/dev/kord/cache/api/observables/Relation.kt b/api/src/commonMain/kotlin/dev/kord/cache/api/observables/Relation.kt new file mode 100644 index 0000000..3b32ca5 --- /dev/null +++ b/api/src/commonMain/kotlin/dev/kord/cache/api/observables/Relation.kt @@ -0,0 +1,35 @@ +package dev.kord.cache.api.observables + +/** + * A typealias for a function that determines the relationship between two entities of type `T` and `R` + * in a uni-directional link. + * The `value` parameter represents the entity of type `T` and the `friend` parameter represents the entity of type `R`. + * The function should return `true` if the two entities are related, or `false` otherwise. + */ +public typealias RelationHandler = (value: T, friend: R) -> Boolean + +/** + * A `Relation` is a uni-directional link between two entities of type `T` and `R`. + * A `Relation` is defined by a set of `RelationHandler`s which determine the relationship + * between two entities. + * + * @param T the type of the first entity in the relation. + */ +public interface Relation { + + /** + * Removes the given [value] from all caches related to this relation. + * + * @param value the entity to remove from the relation. + */ + public suspend fun remove(value: T) + + + /** + * Associates an [Cache] of type `T` with this relation. + * + * @param cache the cache to associate with this relation. + * @param handler the `RelationHandler` that defines the relationship between entities of type `T` and `R`. + */ + public suspend fun to(cache: Cache<*, R>, handler: RelationHandler) +} diff --git a/map/api/map.api b/map/api/map.api index 1155fd1..28b1dcd 100644 --- a/map/api/map.api +++ b/map/api/map.api @@ -40,7 +40,7 @@ public final class dev/kord/cache/map/MapLikeCollection$DefaultImpls { public static fun contains (Ldev/kord/cache/map/MapLikeCollection;Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; } -public final class dev/kord/cache/map/MapLikeCollectionJvm { +public final class dev/kord/cache/map/MapLikeCollectionJvmx { public static final fun weakHashMap (Ldev/kord/cache/map/MapLikeCollection$Companion;)Ldev/kord/cache/map/MapLikeCollection; }