commonMain.InternalServiceLoader.kt Maven / Gradle / Ivy
/*
* Copyright (c) 2024 Oleg Yukhnevich. Use of this source code is governed by the Apache 2.0 license.
*/
@file:Suppress("PackageDirectoryMismatch")
package dev.whyoleg.sweetspi.internal
import kotlin.reflect.*
@RequiresOptIn("Internal API for KSP processor, should not be used outside", RequiresOptIn.Level.ERROR)
public annotation class InternalSweetSpiApi
// there should be one such initializer generated by KSP per module/platform/sourceSet where KSP plugin is applied
@InternalSweetSpiApi
public interface InternalServiceModule {
public val services: Set>
public val requiredServices: Set>
public fun providers(cls: KClass<*>): List<*>
}
@InternalSweetSpiApi
internal expect val internalServiceLoader: Lazy
internal expect open class SynchronizedObject()
internal expect inline fun synchronized(lock: SynchronizedObject, block: () -> T): T
@InternalSweetSpiApi
internal class InternalServiceLoader(
private val modules: List,
) : SynchronizedObject() {
private val registeredServices: Set> = modules.flatMapTo(mutableSetOf(), InternalServiceModule::services)
private val providers = mutableMapOf, List<*>>()
init {
// this could happen mostly if there is something wrong with KSP or runtime code
modules.forEach { module ->
module.requiredServices.forEach { required ->
if (required !in registeredServices) {
println("Service `${required.simpleName}` wasn't registered, required by: $module")
}
}
}
}
fun load(cls: KClass): List {
require(cls in registeredServices) { "Service `${cls.simpleName}` wasn't registered" }
@Suppress("UNCHECKED_CAST")
return synchronized(this) {
providers.getOrPut(cls) { modules.flatMap { it.providers(cls) } } as List
}
}
}