From 5c6c79d8c0e59d6ca8e26daed0c0e9ec1ea875a7 Mon Sep 17 00:00:00 2001 From: Danielle Voznyy Date: Thu, 30 May 2024 22:32:36 -0400 Subject: [PATCH] chore: Update idofront, kotlin 2.0.0, other deps chore: Add shorthand queries up to size 5 chore: Add family filter to any size query feat: Nullable types in shorthand queries --- .gitignore | 1 + addons/geary-autoscan/build.gradle.kts | 4 +- addons/geary-prefabs/build.gradle.kts | 4 +- addons/geary-serialization/build.gradle.kts | 4 +- build.gradle.kts | 2 + geary-benchmarks/build.gradle.kts | 2 +- geary-core/build.gradle.kts | 2 +- .../observers/builders/ObserverBuilder.kt | 6 + .../builders/ObserverEventsBuilder.kt | 8 ++ .../geary/systems/accessors/Accessor.kt | 2 +- .../systems/accessors/AccessorOperations.kt | 12 ++ .../geary/systems/query/QueryShorthands.kt | 117 +++++++++++++++--- .../geary/queries/SimpleQueryTest.kt | 27 ++++ gradle.properties | 2 +- gradle/libs.versions.toml | 19 +-- 15 files changed, 177 insertions(+), 35 deletions(-) diff --git a/.gitignore b/.gitignore index d7d74c04..7231c09a 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,7 @@ build target out */out/production/ +.kotlin .classpath .project diff --git a/addons/geary-autoscan/build.gradle.kts b/addons/geary-autoscan/build.gradle.kts index 78cc277f..9d718d8b 100644 --- a/addons/geary-autoscan/build.gradle.kts +++ b/addons/geary-autoscan/build.gradle.kts @@ -5,8 +5,8 @@ plugins { } dependencies { - compileOnly(project(":geary-core")) - compileOnly(project(":geary-serialization")) + implementation(project(":geary-core")) + implementation(project(":geary-serialization")) implementation(idofrontLibs.reflections) implementation(idofrontLibs.kotlin.reflect) diff --git a/addons/geary-prefabs/build.gradle.kts b/addons/geary-prefabs/build.gradle.kts index bdfd4faa..8bb84b09 100644 --- a/addons/geary-prefabs/build.gradle.kts +++ b/addons/geary-prefabs/build.gradle.kts @@ -8,8 +8,8 @@ kotlin { sourceSets { val commonMain by getting { dependencies { - compileOnly(project(":geary-core")) - compileOnly(project(":geary-serialization")) + implementation(project(":geary-core")) + implementation(project(":geary-serialization")) implementation(libs.uuid) implementation(idofrontLibs.idofront.di) diff --git a/addons/geary-serialization/build.gradle.kts b/addons/geary-serialization/build.gradle.kts index 07fdcbbf..3252a1e9 100644 --- a/addons/geary-serialization/build.gradle.kts +++ b/addons/geary-serialization/build.gradle.kts @@ -8,7 +8,7 @@ kotlin { sourceSets { commonMain { dependencies { - compileOnly(project(":geary-core")) + implementation(project(":geary-core")) implementation(libs.uuid) implementation(idofrontLibs.idofront.di) @@ -20,7 +20,7 @@ kotlin { } jvmMain { dependencies { - compileOnly(idofrontLibs.kotlinx.serialization.kaml) + implementation(idofrontLibs.kotlinx.serialization.kaml) } } } diff --git a/build.gradle.kts b/build.gradle.kts index 5d1e1661..c1ddb768 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -4,6 +4,8 @@ plugins { alias(idofrontLibs.plugins.kotlin.multiplatform) alias(idofrontLibs.plugins.dokka) alias(idofrontLibs.plugins.mia.autoversion) + alias(idofrontLibs.plugins.dependencyversions) + alias(idofrontLibs.plugins.version.catalog.update) } allprojects { diff --git a/geary-benchmarks/build.gradle.kts b/geary-benchmarks/build.gradle.kts index e9ba322b..18fb0a7f 100644 --- a/geary-benchmarks/build.gradle.kts +++ b/geary-benchmarks/build.gradle.kts @@ -13,7 +13,7 @@ configure { dependencies { implementation(project(":geary-core")) - implementation("org.jetbrains.kotlinx:kotlinx-benchmark-runtime:0.4.9") + implementation(libs.kotlinx.benchmark.runtime) } benchmark { diff --git a/geary-core/build.gradle.kts b/geary-core/build.gradle.kts index 98d96a93..d541a01e 100644 --- a/geary-core/build.gradle.kts +++ b/geary-core/build.gradle.kts @@ -13,7 +13,7 @@ kotlin { implementation(idofrontLibs.kotlin.reflect) api(idofrontLibs.idofront.di) - api(libs.kermit) + api(idofrontLibs.kermit) api(idofrontLibs.kotlinx.coroutines) } diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/observers/builders/ObserverBuilder.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/observers/builders/ObserverBuilder.kt index c29b1c67..a05cd797 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/observers/builders/ObserverBuilder.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/observers/builders/ObserverBuilder.kt @@ -18,6 +18,12 @@ interface ExecutableObserver { handle(query) } } + + fun exec(query1: Q1, query2: Q2,handle: Context.(Q1, Q2) -> Unit): Observer { + return filter(query1).filter(query2).exec { + handle(query1, query2) + } + } } data class QueryInvolvingObserverBuilder( diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/observers/builders/ObserverEventsBuilder.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/observers/builders/ObserverEventsBuilder.kt index a05a8a14..d1723bee 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/observers/builders/ObserverEventsBuilder.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/observers/builders/ObserverEventsBuilder.kt @@ -72,6 +72,14 @@ abstract class ObserverEventsBuilder : ExecutableObserver { return ObserverBuilder(this, entityTypeOf(cId(), cId(), cId())) } + inline fun involving(size4: QueryShorthands.Size4? = null): ObserverBuilder { + return ObserverBuilder(this, entityTypeOf(cId(), cId(), cId(), cId())) + } + + inline fun involving(size5: QueryShorthands.Size5? = null): ObserverBuilder { + return ObserverBuilder(this, entityTypeOf(cId(), cId(), cId(), cId(), cId())) + } + fun involvingAny(): ObserverBuilder { return ObserverBuilder(this, entityTypeOf()) } diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/accessors/Accessor.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/accessors/Accessor.kt index 761a528c..ea5137f6 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/accessors/Accessor.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/accessors/Accessor.kt @@ -8,6 +8,6 @@ interface Accessor { val originalAccessor: Accessor? } -interface ReadOnlyAccessor : Accessor, ReadOnlyProperty +interface ReadOnlyAccessor : Accessor, ReadOnlyProperty interface ReadWriteAccessor : ReadOnlyAccessor, ReadWriteProperty diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/accessors/AccessorOperations.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/accessors/AccessorOperations.kt index 50bbf92d..770fb035 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/accessors/AccessorOperations.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/accessors/AccessorOperations.kt @@ -9,6 +9,7 @@ import com.mineinabyss.geary.helpers.componentId import com.mineinabyss.geary.helpers.componentIdWithNullable import com.mineinabyss.geary.systems.accessors.type.* import com.mineinabyss.geary.systems.query.QueriedEntity +import kotlin.reflect.typeOf abstract class AccessorOperations { abstract val cacheAccessors: Boolean @@ -26,6 +27,17 @@ abstract class AccessorOperations { } } + protected inline fun QueriedEntity.getPotentiallyNullable(): ReadOnlyAccessor { + val t = typeOf() + return addAccessor { + val id = componentId(t).withRole(HOLDS_DATA) + val compAccessor = ComponentAccessor(null, id) + if(t.isMarkedNullable) + ComponentOrDefaultAccessor(compAccessor, id) { null } + else compAccessor + } as ReadOnlyAccessor + } + /** Accesses a data stored in a relation with kind [K] and target type [T], ensuring it is on the entity. */ protected inline fun QueriedEntity.getRelation(): ComponentAccessor { return addAccessor { ComponentAccessor(null, Relation.of().id) } diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/query/QueryShorthands.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/query/QueryShorthands.kt index 6be04d07..8895818e 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/query/QueryShorthands.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/query/QueryShorthands.kt @@ -36,13 +36,40 @@ abstract class ShorthandQuery3 : ShorthandQuery() { abstract operator fun component3(): C } +abstract class ShorthandQuery4 : ShorthandQuery() { + abstract val comp1: A + abstract val comp2: B + abstract val comp3: C + abstract val comp4: D + + abstract operator fun component1(): A + abstract operator fun component2(): B + abstract operator fun component3(): C + abstract operator fun component4(): D +} + +abstract class ShorthandQuery5 : ShorthandQuery() { + abstract val comp1: A + abstract val comp2: B + abstract val comp3: C + abstract val comp4: D + abstract val comp5: E + + abstract operator fun component1(): A + abstract operator fun component2(): B + abstract operator fun component3(): C + abstract operator fun component4(): D + abstract operator fun component5(): E +} + + fun query() = object : Query() {} fun query(match: MutableFamily.Selector.And.() -> Unit) = object : Query() { override fun ensure() = this { add(family(match)) } } -inline fun query( +inline fun query( size1: QueryShorthands.Size1? = null, noinline filterFamily: (MutableFamily.Selector.And.() -> Unit)? = null ) = object : ShorthandQuery1() { @@ -51,36 +78,88 @@ inline fun query( filterFamily?.let { this { it() } } } - override val comp1 by get() + override val comp1 by getPotentiallyNullable() override fun component1() = comp1 } -inline fun query(size2: QueryShorthands.Size2? = null) = - object : ShorthandQuery2() { - override val involves = entityTypeOf(cId(), cId()) +inline fun query( + size2: QueryShorthands.Size2? = null, + noinline filterFamily: (MutableFamily.Selector.And.() -> Unit)? = null, +) = object : ShorthandQuery2() { + override val involves = entityTypeOf(cId(), cId()) + override fun ensure() { + filterFamily?.let { this { it() } } + } + + override val comp1 by getPotentiallyNullable() + override val comp2 by getPotentiallyNullable() + + override fun component1(): A = comp1 + override fun component2(): B = comp2 +} - override val comp1 by get() - override val comp2 by get() - override fun component1(): A = comp1 - override fun component2(): B = comp2 +inline fun query( + size3: QueryShorthands.Size3? = null, + noinline filterFamily: (MutableFamily.Selector.And.() -> Unit)? = null, +) = object : ShorthandQuery3() { + override val involves = entityTypeOf(cId(), cId(), cId()) + override fun ensure() { + filterFamily?.let { this { it() } } } + override val comp1 by getPotentiallyNullable() + override val comp2 by getPotentiallyNullable() + override val comp3 by getPotentiallyNullable() -inline fun query(size3: QueryShorthands.Size3? = null) = - object : ShorthandQuery3() { - override val involves = entityTypeOf(cId(), cId(), cId()) + override fun component1(): A = comp1 + override fun component2(): B = comp2 + override fun component3(): C = comp3 +} - override val comp1 by get() - override val comp2 by get() - override val comp3 by get() +inline fun query( + size4: QueryShorthands.Size4? = null, + noinline filterFamily: (MutableFamily.Selector.And.() -> Unit)? = null, +) = object : ShorthandQuery4() { + override val involves = entityTypeOf(cId(), cId(), cId(), cId()) + override fun ensure() { + filterFamily?.let { this { it() } } + } + + override val comp1 by getPotentiallyNullable() + override val comp2 by getPotentiallyNullable() + override val comp3 by getPotentiallyNullable() + override val comp4 by getPotentiallyNullable() + + override fun component1(): A = comp1 + override fun component2(): B = comp2 + override fun component3(): C = comp3 + override fun component4(): D = comp4 +} - override fun component1(): A = comp1 - override fun component2(): B = comp2 - override fun component3(): C = comp3 +inline fun query( + size5: QueryShorthands.Size5? = null, + noinline filterFamily: (MutableFamily.Selector.And.() -> Unit)? = null, +) = object : ShorthandQuery5() { + override val involves = entityTypeOf(cId(), cId(), cId(), cId()) + override fun ensure() { + filterFamily?.let { this { it() } } } + override val comp1 by getPotentiallyNullable() + override val comp2 by getPotentiallyNullable() + override val comp3 by getPotentiallyNullable() + override val comp4 by getPotentiallyNullable() + override val comp5 by getPotentiallyNullable() + + override fun component1(): A = comp1 + override fun component2(): B = comp2 + override fun component3(): C = comp3 + override fun component4(): D = comp4 + override fun component5(): E = comp5 +} + @JvmName("toList1") inline fun CachedQuery>.toList(): List = map { it.component1() } @@ -95,4 +174,6 @@ object QueryShorthands { sealed class Size1 sealed class Size2 sealed class Size3 + sealed class Size4 + sealed class Size5 } diff --git a/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/queries/SimpleQueryTest.kt b/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/queries/SimpleQueryTest.kt index 2d2e0036..f4292505 100644 --- a/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/queries/SimpleQueryTest.kt +++ b/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/queries/SimpleQueryTest.kt @@ -5,7 +5,14 @@ import com.mineinabyss.geary.helpers.entity import com.mineinabyss.geary.helpers.tests.GearyTest import com.mineinabyss.geary.modules.geary import com.mineinabyss.geary.systems.query.Query +import com.mineinabyss.geary.systems.query.query +import com.mineinabyss.geary.systems.query.toList +import io.kotest.assertions.assertSoftly import io.kotest.assertions.throwables.shouldThrow +import io.kotest.assertions.withClue +import io.kotest.matchers.collections.shouldContain +import io.kotest.matchers.collections.shouldContainAll +import io.kotest.matchers.collections.shouldHaveSize import io.kotest.matchers.shouldBe import org.junit.jupiter.api.BeforeAll import kotlin.test.Test @@ -94,4 +101,24 @@ class SimpleQueryTest : GearyTest() { }.map { it.int.toString() }.toList() } } + + @Test + fun `should allow querying nullable types as optional in shorthand queries`() { + entity { + set(1) + set("Both") + } + entity { + set("Only string") + } + val query = geary.queryManager.trackQuery(query()) + val matched = query.toList() + assertSoftly(matched) { + shouldContainAll((0..9 step 2).map { it to null }) + shouldContain(1 to "Both") + withClue("Should not include anything else") { + shouldHaveSize(11) + } + } + } } diff --git a/gradle.properties b/gradle.properties index 15b28394..f426544d 100644 --- a/gradle.properties +++ b/gradle.properties @@ -2,5 +2,5 @@ group=com.mineinabyss version=0.26 # Workaround for dokka builds failing on CI, see https://github.com/Kotlin/dokka/issues/1405 #org.gradle.jvmargs=-XX:MaxMetaspaceSize=512m -idofrontVersion=0.24.0-dev.2 +idofrontVersion=0.24.0 kotlin.native.ignoreDisabledTargets=true diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 0bfbcf83..08dfbc80 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,12 +1,17 @@ [versions] +androidxCollection = "1.4.0" atomicfu = "0.24.0" +kotlinxBenchmarkRuntime = "0.4.10" +okio = "3.9.0" +roaringbitmap = "1.0.6" +statelyConcurrency = "2.0.7" +uuid = "0.8.4" [libraries] +androidx-collection = { module = "androidx.collection:collection", version.ref = "androidxCollection" } atomicfu = { module = "org.jetbrains.kotlinx:atomicfu", version.ref = "atomicfu" } -uuid = "com.benasher44:uuid:0.8.1" -okio = "com.squareup.okio:okio:3.5.0" -roaringbitmap = "org.roaringbitmap:RoaringBitmap:0.9.32" -bitvector = "net.onedaybeard.bitvector:bitvector-js:0.1.4" -kermit = "co.touchlab:kermit:1.2.2" -androidx-collection = "androidx.collection:collection:1.4.0" -stately-concurrency = "co.touchlab:stately-concurrency:2.0.7" +kotlinx-benchmark-runtime = { module = "org.jetbrains.kotlinx:kotlinx-benchmark-runtime", version.ref = "kotlinxBenchmarkRuntime" } +okio = { module = "com.squareup.okio:okio", version.ref = "okio" } +roaringbitmap = { module = "org.roaringbitmap:RoaringBitmap", version.ref = "roaringbitmap" } +stately-concurrency = { module = "co.touchlab:stately-concurrency", version.ref = "statelyConcurrency" } +uuid = { module = "com.benasher44:uuid", version.ref = "uuid" }