From 34334396178eec3f72f1228b400cb9ec81c4ce4c Mon Sep 17 00:00:00 2001 From: Linnea Gräf Date: Tue, 7 Jan 2025 14:07:14 +0100 Subject: build: Split up dependency injection into its own package --- build-src/README.md | 2 + build-src/build.gradle.kts | 11 ++ build-src/settings.gradle.kts | 0 build-src/src/main/kotlin/GenerateItemIds.kt | 72 ++++++++++ build-src/src/main/kotlin/RepoDownload.kt | 41 ++++++ build-src/src/main/kotlin/ledger-marker.gradle.kts | 0 build-src/src/main/kotlin/ledger-repo.gradle.kts | 1 + build.gradle.kts | 156 ++++++--------------- dependency-injection/build.gradle.kts | 8 ++ .../src/main/kotlin/moe/nea/ledger/utils/di/DI.kt | 83 +++++++++++ .../kotlin/moe/nea/ledger/utils/di/DIProvider.kt | 56 ++++++++ .../main/kotlin/moe/nea/ledger/utils/di/Inject.kt | 6 + settings.gradle.kts | 2 + src/main/kotlin/moe/nea/ledger/utils/di/DI.kt | 78 ----------- .../kotlin/moe/nea/ledger/utils/di/DIProvider.kt | 52 ------- src/main/kotlin/moe/nea/ledger/utils/di/Inject.kt | 6 - 16 files changed, 323 insertions(+), 251 deletions(-) create mode 100644 build-src/README.md create mode 100644 build-src/build.gradle.kts create mode 100644 build-src/settings.gradle.kts create mode 100644 build-src/src/main/kotlin/GenerateItemIds.kt create mode 100644 build-src/src/main/kotlin/RepoDownload.kt create mode 100644 build-src/src/main/kotlin/ledger-marker.gradle.kts create mode 100644 build-src/src/main/kotlin/ledger-repo.gradle.kts create mode 100644 dependency-injection/build.gradle.kts create mode 100644 dependency-injection/src/main/kotlin/moe/nea/ledger/utils/di/DI.kt create mode 100644 dependency-injection/src/main/kotlin/moe/nea/ledger/utils/di/DIProvider.kt create mode 100644 dependency-injection/src/main/kotlin/moe/nea/ledger/utils/di/Inject.kt delete mode 100644 src/main/kotlin/moe/nea/ledger/utils/di/DI.kt delete mode 100644 src/main/kotlin/moe/nea/ledger/utils/di/DIProvider.kt delete mode 100644 src/main/kotlin/moe/nea/ledger/utils/di/Inject.kt diff --git a/build-src/README.md b/build-src/README.md new file mode 100644 index 0000000..66d45bc --- /dev/null +++ b/build-src/README.md @@ -0,0 +1,2 @@ + +Intentionally not using `buildSrc` over an `includeBuild("build-src")` due to better performance of composite builds. diff --git a/build-src/build.gradle.kts b/build-src/build.gradle.kts new file mode 100644 index 0000000..f203fd6 --- /dev/null +++ b/build-src/build.gradle.kts @@ -0,0 +1,11 @@ +plugins { + kotlin("jvm") version "2.0.20" + `kotlin-dsl` +} +repositories { + mavenCentral() +} +dependencies { + implementation("com.google.code.gson:gson:2.11.0") + implementation(gradleApi()) +} diff --git a/build-src/settings.gradle.kts b/build-src/settings.gradle.kts new file mode 100644 index 0000000..e69de29 diff --git a/build-src/src/main/kotlin/GenerateItemIds.kt b/build-src/src/main/kotlin/GenerateItemIds.kt new file mode 100644 index 0000000..24f2f62 --- /dev/null +++ b/build-src/src/main/kotlin/GenerateItemIds.kt @@ -0,0 +1,72 @@ +import com.google.gson.Gson +import org.gradle.api.DefaultTask +import org.gradle.api.file.DirectoryProperty +import org.gradle.api.provider.Property +import org.gradle.api.tasks.Input +import org.gradle.api.tasks.InputDirectory +import org.gradle.api.tasks.Internal +import org.gradle.api.tasks.OutputDirectory +import org.gradle.api.tasks.TaskAction +import java.io.File + +abstract class GenerateItemIds : DefaultTask() { + @get: OutputDirectory + abstract val outputDirectory: DirectoryProperty + + @get: InputDirectory + abstract val repoFiles: DirectoryProperty + + @get: Input + abstract val repoHash: Property + + @get: Input + abstract val packageName: Property + + @get:Internal + val outputFile get() = outputDirectory.asFile.get().resolve(packageName.get().replace(".", "/") + "/ItemIds.java") + + init { + repoHash.convention("unknown-repo-git-hash") + } + + @TaskAction + fun generateItemIds() { + val nonIdName = "[^A-Z0-9_]".toRegex() + + data class Item(val id: String, val file: File) { + val javaName get() = id.replace(nonIdName, { "__" + it.value.single().code }) + } + + val items = mutableListOf() + for (listFile in repoFiles.asFile.get().resolve("items").listFiles() ?: emptyArray()) { + listFile ?: continue + if (listFile.extension != "json") { + error("Unknown file $listFile") + } + items.add(Item(listFile.nameWithoutExtension, listFile)) + } + items.sortedBy { it.id } + outputFile.parentFile.mkdirs() + val writer = outputFile.writer().buffered() + writer.appendLine("// @generated from " + repoHash.get()) + writer.appendLine("package " + packageName.get() + ";") + writer.appendLine() + writer.appendLine("import moe.nea.ledger.ItemId;") + writer.appendLine() + writer.appendLine("/**") + writer.appendLine(" * Automatically generated {@link ItemId} list.") + writer.appendLine(" */") + writer.appendLine("@org.jspecify.annotations.NullMarked") + writer.appendLine("public interface ItemIds {") + val gson = Gson() + for (item in items) { + writer.appendLine("\t/**") + writer.appendLine("\t * @see JSON definition") + writer.appendLine("\t */") + writer.appendLine("\tItemId ${item.javaName} =" + + " ItemId.forName(${gson.toJson(item.id)});") + } + writer.appendLine("}") + writer.close() + } +} diff --git a/build-src/src/main/kotlin/RepoDownload.kt b/build-src/src/main/kotlin/RepoDownload.kt new file mode 100644 index 0000000..182c66f --- /dev/null +++ b/build-src/src/main/kotlin/RepoDownload.kt @@ -0,0 +1,41 @@ +import org.gradle.api.DefaultTask +import org.gradle.api.file.DirectoryProperty +import org.gradle.api.provider.Property +import org.gradle.api.tasks.Input +import org.gradle.api.tasks.OutputDirectory +import org.gradle.api.tasks.TaskAction +import java.net.URI +import java.util.zip.ZipInputStream + +abstract class RepoDownload : DefaultTask() { + @get:Input + abstract val hash: Property + + @get:OutputDirectory + abstract val outputDirectory: DirectoryProperty + + init { + outputDirectory.convention(project.layout.buildDirectory.dir("extracted-test-repo")) + } + + @TaskAction + fun performDownload() { + val outputDir = outputDirectory.asFile.get().absoluteFile + outputDir.mkdirs() + URI("https://github.com/notEnoughUpdates/notEnoughUpdates-rEPO/archive/${hash.get()}.zip").toURL().openStream() + .let(::ZipInputStream) + .use { zipInput -> + while (true) { + val entry = zipInput.nextEntry ?: break + val destination = outputDir.resolve( + entry.name.substringAfter('/')).absoluteFile + require(outputDir in generateSequence(destination) { it.parentFile }) + if (entry.isDirectory) continue + destination.parentFile.mkdirs() + destination.outputStream().use { output -> + zipInput.copyTo(output) + } + } + } + } +} \ No newline at end of file diff --git a/build-src/src/main/kotlin/ledger-marker.gradle.kts b/build-src/src/main/kotlin/ledger-marker.gradle.kts new file mode 100644 index 0000000..e69de29 diff --git a/build-src/src/main/kotlin/ledger-repo.gradle.kts b/build-src/src/main/kotlin/ledger-repo.gradle.kts new file mode 100644 index 0000000..1a9be63 --- /dev/null +++ b/build-src/src/main/kotlin/ledger-repo.gradle.kts @@ -0,0 +1 @@ +tasks.register("downloadRepo", RepoDownload::class) diff --git a/build.gradle.kts b/build.gradle.kts index a5173a1..cb69ab4 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,12 +1,8 @@ import com.github.gmazzo.buildconfig.BuildConfigExtension import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar -import com.google.gson.Gson -import com.google.gson.JsonObject import org.apache.commons.lang3.SystemUtils import proguard.gradle.ProGuardTask import java.io.ByteArrayOutputStream -import java.net.URI -import java.util.zip.ZipInputStream buildscript { repositories { @@ -25,6 +21,8 @@ plugins { id("com.github.johnrengelman.shadow") version "8.1.1" id("com.github.gmazzo.buildconfig") version "5.5.0" kotlin("jvm") version "2.0.20" + id("ledger-marker") + id("ledger-repo") } fun cmd(vararg args: String): String { @@ -41,9 +39,11 @@ val baseGroup: String by project val mcVersion: String by project val gitVersion = cmd("git", "rev-parse", "--short", "HEAD") val fullVersion = project.property("mod_version").toString() -val version: String = "$fullVersion-$gitVersion" +val versionName: String = "$fullVersion-$gitVersion" val mixinGroup = "$baseGroup.mixin" -project.version = version +allprojects { + version = versionName +} val modid: String by project // Toolchains: @@ -89,14 +89,21 @@ sourceSets.main { kotlin.destinationDirectory.set(java.destinationDirectory) } -repositories { - mavenCentral() - maven("https://repo.nea.moe/releases/") - maven("https://repo.spongepowered.org/maven/") - maven("https://maven.notenoughupdates.org/releases") - maven("https://pkgs.dev.azure.com/djtheredstoner/DevAuth/_packaging/public/maven/v1") +allprojects { + repositories { + mavenCentral() + maven("https://repo.nea.moe/releases/") + maven("https://repo.spongepowered.org/maven/") + maven("https://maven.notenoughupdates.org/releases") + maven("https://pkgs.dev.azure.com/djtheredstoner/DevAuth/_packaging/public/maven/v1") + } } +// TODO: Add an extra shadow configuration for optimizable jars +//val optShadowImpl: Configuration by configurations.creating { +// +//} + val shadowImpl: Configuration by configurations.creating { configurations.implementation.get().extendsFrom(this) } @@ -117,6 +124,7 @@ dependencies { shadowImpl("org.xerial:sqlite-jdbc:3.45.3.0") shadowImpl("org.notenoughupdates.moulconfig:legacy:3.0.0-beta.9") shadowImpl("io.azam.ulidj:ulidj:1.0.4") + shadowImpl(project(":dependency-injection")) shadowImpl("moe.nea:libautoupdate:1.3.1") runtimeOnly("me.djtheredstoner:DevAuth-forge-legacy:1.1.2") testImplementation("org.junit.jupiter:junit-jupiter:5.9.2") @@ -129,114 +137,25 @@ tasks.shadowJar { doFirst { error("Incorrect shadow JAR built!") } } -tasks.test { - useJUnitPlatform() -} - -tasks.withType(JavaCompile::class) { - options.encoding = "UTF-8" -} - -abstract class GenerateItemIds : DefaultTask() { - @get: OutputDirectory - abstract val outputDirectory: DirectoryProperty - - @get: InputDirectory - abstract val repoFiles: DirectoryProperty - - @get: Input - abstract val repoHash: Property - - @get: Input - abstract val packageName: Property - - @get:Internal - val outputFile get() = outputDirectory.asFile.get().resolve(packageName.get().replace(".", "/") + "/ItemIds.java") - - @TaskAction - fun generateItemIds() { - val nonIdName = "[^A-Z0-9_]".toRegex() - - data class Item(val id: String, val file: File) { - val javaName get() = id.replace(nonIdName, { "__" + it.value.single().code }) - } - - val items = mutableListOf() - for (listFile in repoFiles.asFile.get().resolve("items").listFiles() ?: emptyArray()) { - listFile ?: continue - if (listFile.extension != "json") { - error("Unknown file $listFile") - } - items.add(Item(listFile.nameWithoutExtension, listFile)) - } - items.sortedBy { it.id } - outputFile.parentFile.mkdirs() - val writer = outputFile.writer().buffered() - writer.appendLine("// @generated from " + repoHash.get()) - writer.appendLine("package " + packageName.get() + ";") - writer.appendLine() - writer.appendLine("import moe.nea.ledger.ItemId;") - writer.appendLine() - writer.appendLine("/**") - writer.appendLine(" * Automatically generated {@link ItemId} list.") - writer.appendLine(" */") - writer.appendLine("@org.jspecify.annotations.NullMarked") - writer.appendLine("public interface ItemIds {") - val gson = Gson() - for (item in items) { - writer.appendLine("\t/**") - writer.appendLine("\t * @see JSON definition") - writer.appendLine("\t */") - writer.appendLine("\tItemId ${item.javaName} =" + - " ItemId.forName(${gson.toJson(item.id)});") - } - writer.appendLine("}") - writer.close() - } -} - -abstract class RepoDownload : DefaultTask() { - @get:Input - abstract val hash: Property - - @get:OutputDirectory - abstract val outputDirectory: DirectoryProperty - - init { - outputDirectory.convention(project.layout.buildDirectory.dir("extracted-test-repo")) +allprojects { + tasks.withType { + useJUnitPlatform() } - @TaskAction - fun performDownload() { - val outputDir = outputDirectory.asFile.get().absoluteFile - outputDir.mkdirs() - URI("https://github.com/notEnoughUpdates/notEnoughUpdates-rEPO/archive/${hash.get()}.zip").toURL().openStream() - .let(::ZipInputStream) - .use { zipInput -> - while (true) { - val entry = zipInput.nextEntry ?: break - val destination = outputDir.resolve( - entry.name.substringAfter('/')).absoluteFile - require(outputDir in generateSequence(destination) { it.parentFile }) - if (entry.isDirectory) continue - destination.parentFile.mkdirs() - destination.outputStream().use { output -> - zipInput.copyTo(output) - } - } - } + tasks.withType(JavaCompile::class) { + options.encoding = "UTF-8" } } -val downloadRepo by tasks.register("downloadRepo", RepoDownload::class) { +tasks.downloadRepo { hash.set("dcf1dbc") } val generateItemIds by tasks.register("generateItemIds", GenerateItemIds::class) { - repoHash.set(downloadRepo.hash) + repoHash.set(tasks.downloadRepo.get().hash) packageName.set("moe.nea.ledger.gen") outputDirectory.set(layout.buildDirectory.dir("generated/sources/itemIds")) - repoFiles.set(downloadRepo.outputDirectory) + repoFiles.set(tasks.downloadRepo.get().outputDirectory) } sourceSets.main { java.srcDir(generateItemIds) @@ -278,7 +197,6 @@ val proguard = tasks.register("proguard", ProGuardTask::class) { val libJava = javaToolchains.launcherFor(java.toolchain) .get() .metadata.installationPath.file("jre/lib/rt.jar") - println(libJava) libraryjars(libJava) libraryjars(configurations.compileClasspath) } @@ -313,10 +231,18 @@ tasks.jar { tasks.assemble.get().dependsOn(tasks.remapJar) -configure { - packageName("moe.nea.ledger.gen") - buildConfigField("VERSION", version) - buildConfigField("FULL_VERSION", fullVersion) - buildConfigField("GIT_COMMIT", gitVersion) +inline fun ExtensionAware.configureIf(crossinline block: T.() -> Unit) { + if (extensions.findByType() != null) { + extensions.configure { block() } + } +} + +allprojects { + configureIf { + packageName("moe.nea.ledger.gen") + buildConfigField("VERSION", versionName) + buildConfigField("FULL_VERSION", fullVersion) + buildConfigField("GIT_COMMIT", gitVersion) + } } diff --git a/dependency-injection/build.gradle.kts b/dependency-injection/build.gradle.kts new file mode 100644 index 0000000..5a51941 --- /dev/null +++ b/dependency-injection/build.gradle.kts @@ -0,0 +1,8 @@ +plugins { + `java-library` + kotlin("jvm") +} + +java { + toolchain.languageVersion.set(JavaLanguageVersion.of(8)) +} diff --git a/dependency-injection/src/main/kotlin/moe/nea/ledger/utils/di/DI.kt b/dependency-injection/src/main/kotlin/moe/nea/ledger/utils/di/DI.kt new file mode 100644 index 0000000..0683063 --- /dev/null +++ b/dependency-injection/src/main/kotlin/moe/nea/ledger/utils/di/DI.kt @@ -0,0 +1,83 @@ +package moe.nea.ledger.utils.di + +import java.lang.reflect.AnnotatedElement +import java.util.Collections +import java.util.Stack + +@Suppress("UNCHECKED_CAST") +class DI { + private fun formatInjectionStack() = + injectionStack.joinToString(" -> ") + + fun getProvider(type: Class): BaseDIProvider { + val provider = providers[type] as BaseDIProvider? + ?: error("Could not find provider for type $type") + return provider + } + + private fun internalProvide(type: Class, element: AnnotatedElement? = null): T { + try { + val provider = getProvider(type) as BaseDIProvider + val context = if (element == null) provider.createEmptyContext() else provider.createContext(element) + val key = Pair(type, context) + val existingValue = values[key] + if (existingValue != null) return existingValue as T + if (type in injectionStack) { + error("Found injection cycle: ${formatInjectionStack()} -> $type") + } + injectionStack.push(type) + val value = + provider.provideWithContext(this, context) + val cycleCheckCookie = injectionStack.pop() + require(cycleCheckCookie == type) { "Unbalanced stack cookie: $cycleCheckCookie != $type" } + values[key] = value + return value + } catch (ex: Exception) { + throw RuntimeException("Could not create instance for type $type (in stack ${formatInjectionStack()})", ex) + } + } + + fun provide(type: Class, element: AnnotatedElement? = null): T { + return internalProvide(type, element) + } + + inline fun provide(): T = provide(T::class.java) + + fun register(type: Class, provider: BaseDIProvider) { + providers[type] = provider + } + + fun registerInjectableInterface(parent: Class, type: Class) { + internalRegisterInjectableClass(type) + register(parent, DIProvider.fromInheritance(type)) + } + + fun registerInjectableClasses(vararg type: Class<*>) { + type.forEach { internalRegisterInjectableClass(it) } + } + + private fun internalRegisterInjectableClass(type: Class) { + register(type, DIProvider.fromInjectableClass(type)) + } + + fun instantiateAll() { + providers.keys.forEach { + provide(it, null) + } + } + + fun getAllInstances(): Collection = + Collections.unmodifiableCollection(values.values) + + fun registerSingleton(value: T) { + register(value.javaClass, DIProvider.singeleton(value)) + } + + private val injectionStack: Stack> = Stack() + private val values = mutableMapOf, *>, Any>() + private val providers = mutableMapOf, BaseDIProvider<*, *>>() + + init { + registerSingleton(this) + } +} \ No newline at end of file diff --git a/dependency-injection/src/main/kotlin/moe/nea/ledger/utils/di/DIProvider.kt b/dependency-injection/src/main/kotlin/moe/nea/ledger/utils/di/DIProvider.kt new file mode 100644 index 0000000..8a54d5f --- /dev/null +++ b/dependency-injection/src/main/kotlin/moe/nea/ledger/utils/di/DIProvider.kt @@ -0,0 +1,56 @@ +package moe.nea.ledger.utils.di + +import java.lang.reflect.AnnotatedElement +import java.lang.reflect.Constructor + +fun interface DIProvider : BaseDIProvider { + override fun provideWithContext(di: DI, context: Unit): T { + return provide(di) + } + + override fun createContext(element: AnnotatedElement) { + } + + override fun createEmptyContext() { + } + + fun provide(di: DI): T + + companion object { + + fun fromInjectableClass(clazz: Class): DIProvider { + @Suppress("UNCHECKED_CAST") + val cons = (clazz.constructors.find { it.getAnnotation(Inject::class.java) != null } + ?: clazz.constructors.find { it.parameterCount == 0 } + ?: error("Could not find DI injection entrypoint for class $clazz")) + as Constructor + // TODO: consider using unsafe init to inject the parameters *before* calling the constructor + return DIProvider { di -> + val typArgs = cons.parameters.map { + di.provide(it.type, it) + }.toTypedArray() + val instance = cons.newInstance(*typArgs) + for (it in clazz.fields) { + if (it.getAnnotation(Inject::class.java) == null) continue + it.set(instance, di.provide(it.type, it)) + } + instance + } + } + + fun singeleton(value: T): DIProvider { + return DIProvider { _ -> value } + } + + fun fromInheritance(type: Class): DIProvider { + return DIProvider { it.provide(type) } + } + } + +} + +interface BaseDIProvider { + fun createContext(element: AnnotatedElement): C + fun provideWithContext(di: DI, context: C): T + fun createEmptyContext(): C +} diff --git a/dependency-injection/src/main/kotlin/moe/nea/ledger/utils/di/Inject.kt b/dependency-injection/src/main/kotlin/moe/nea/ledger/utils/di/Inject.kt new file mode 100644 index 0000000..a8fdd87 --- /dev/null +++ b/dependency-injection/src/main/kotlin/moe/nea/ledger/utils/di/Inject.kt @@ -0,0 +1,6 @@ +package moe.nea.ledger.utils.di + +@Retention(AnnotationRetention.RUNTIME) +@Target(AnnotationTarget.CONSTRUCTOR, AnnotationTarget.FIELD) +annotation class Inject( +) diff --git a/settings.gradle.kts b/settings.gradle.kts index 82d99b6..28eae3b 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -24,3 +24,5 @@ plugins { rootProject.name = "ledger" +include("dependency-injection") +includeBuild("build-src") diff --git a/src/main/kotlin/moe/nea/ledger/utils/di/DI.kt b/src/main/kotlin/moe/nea/ledger/utils/di/DI.kt deleted file mode 100644 index a9061d7..0000000 --- a/src/main/kotlin/moe/nea/ledger/utils/di/DI.kt +++ /dev/null @@ -1,78 +0,0 @@ -package moe.nea.ledger.utils.di - -import java.lang.reflect.AnnotatedElement -import java.util.Collections -import java.util.Stack - -@Suppress("UNCHECKED_CAST") -class DI { - private fun formatInjectionStack() = - injectionStack.joinToString(" -> ") - - fun getProvider(type: Class): BaseDIProvider { - val provider = providers[type] as BaseDIProvider? - ?: error("Could not find provider for type $type") - return provider - } - - private fun internalProvide(type: Class, element: AnnotatedElement? = null): T { - try { - val provider = getProvider(type) as BaseDIProvider - val context = if (element == null) provider.createEmptyContext() else provider.createContext(element) - val key = Pair(type, context) - val existingValue = values[key] - if (existingValue != null) return existingValue as T - if (type in injectionStack) { - error("Found injection cycle: ${formatInjectionStack()} -> $type") - } - injectionStack.push(type) - val value = - provider.provideWithContext(this, context) - val cycleCheckCookie = injectionStack.pop() - require(cycleCheckCookie == type) { "Unbalanced stack cookie: $cycleCheckCookie != $type" } - values[key] = value - return value - } catch (ex: Exception) { - throw RuntimeException("Could not create instance for type $type (in stack ${formatInjectionStack()})", ex) - } - } - - fun provide(type: Class, element: AnnotatedElement? = null): T { - return internalProvide(type, element) - } - - inline fun provide(): T = provide(T::class.java) - - fun register(type: Class, provider: BaseDIProvider) { - providers[type] = provider - } - - fun registerInjectableClasses(vararg type: Class<*>) { - type.forEach { internalRegisterInjectableClass(it) } - } - - private fun internalRegisterInjectableClass(type: Class) { - register(type, DIProvider.fromInjectableClass(type)) - } - - fun instantiateAll() { - providers.keys.forEach { - provide(it, null) - } - } - - fun getAllInstances(): Collection = - Collections.unmodifiableCollection(values.values) - - fun registerSingleton(value: T) { - register(value.javaClass, DIProvider.singeleton(value)) - } - - private val injectionStack: Stack> = Stack() - private val values = mutableMapOf, *>, Any>() - private val providers = mutableMapOf, BaseDIProvider<*, *>>() - - init { - registerSingleton(this) - } -} \ No newline at end of file diff --git a/src/main/kotlin/moe/nea/ledger/utils/di/DIProvider.kt b/src/main/kotlin/moe/nea/ledger/utils/di/DIProvider.kt deleted file mode 100644 index bd5b9ef..0000000 --- a/src/main/kotlin/moe/nea/ledger/utils/di/DIProvider.kt +++ /dev/null @@ -1,52 +0,0 @@ -package moe.nea.ledger.utils.di - -import java.lang.reflect.AnnotatedElement -import java.lang.reflect.Constructor - -fun interface DIProvider : BaseDIProvider { - override fun provideWithContext(di: DI, context: Unit): T { - return provide(di) - } - - override fun createContext(element: AnnotatedElement) { - } - - override fun createEmptyContext() { - } - - fun provide(di: DI): T - - companion object { - - fun fromInjectableClass(clazz: Class): DIProvider { - @Suppress("UNCHECKED_CAST") - val cons = (clazz.constructors.find { it.getAnnotation(Inject::class.java) != null } - ?: clazz.constructors.find { it.parameterCount == 0 } - ?: error("Could not find DI injection entrypoint for class $clazz")) - as Constructor - // TODO: consider using unsafe init to inject the parameters *before* calling the constructor - return DIProvider { di -> - val typArgs = cons.parameters.map { - di.provide(it.type, it) - }.toTypedArray() - val instance = cons.newInstance(*typArgs) - for (it in clazz.fields) { - if (it.getAnnotation(Inject::class.java) == null) continue - it.set(instance, di.provide(it.type, it)) - } - instance - } - } - - fun singeleton(value: T): DIProvider { - return DIProvider { _ -> value } - } - } - -} - -interface BaseDIProvider { - fun createContext(element: AnnotatedElement): C - fun provideWithContext(di: DI, context: C): T - fun createEmptyContext(): C -} diff --git a/src/main/kotlin/moe/nea/ledger/utils/di/Inject.kt b/src/main/kotlin/moe/nea/ledger/utils/di/Inject.kt deleted file mode 100644 index a8fdd87..0000000 --- a/src/main/kotlin/moe/nea/ledger/utils/di/Inject.kt +++ /dev/null @@ -1,6 +0,0 @@ -package moe.nea.ledger.utils.di - -@Retention(AnnotationRetention.RUNTIME) -@Target(AnnotationTarget.CONSTRUCTOR, AnnotationTarget.FIELD) -annotation class Inject( -) -- cgit