All Downloads are FREE. Search and download functionalities are using the official Maven repository.

commonMain.korlibs.inject.AsyncInjector.kt Maven / Gradle / Ivy

There is a newer version: 4.0.10
Show newest version
package korlibs.inject

import kotlinx.coroutines.*
import kotlin.coroutines.*
import kotlin.reflect.*

interface AsyncObjectProvider {
    suspend fun get(injector: AsyncInjector): T
    suspend fun deinit()
}

class PrototypeAsyncObjectProvider(val generator: suspend AsyncInjector.() -> T) : AsyncObjectProvider {
    override suspend fun get(injector: AsyncInjector): T = injector.created(generator(injector))
    override suspend fun deinit() = Unit
    override fun toString(): String = "PrototypeAsyncObjectProvider()"
}

class FactoryAsyncObjectProvider(val generator: suspend AsyncInjector.() -> AsyncFactory) :
    AsyncObjectProvider {
    override suspend fun get(injector: AsyncInjector): T = injector.created(generator(injector).create())
    override suspend fun deinit() = Unit
    override fun toString(): String = "FactoryAsyncObjectProvider()"
}

class SingletonAsyncObjectProvider(val generator: suspend AsyncInjector.() -> T) : AsyncObjectProvider {
    var value: T? = null
    override suspend fun get(injector: AsyncInjector): T {
        if (value == null) value = injector.created(generator(injector))
        return value!!
    }
    override suspend fun deinit() {
        (value as? AsyncDestructor?)?.deinit()
    }

    override fun toString(): String = "SingletonAsyncObjectProvider($value)"
}

class InstanceAsyncObjectProvider(val instance: T) : AsyncObjectProvider {
    override suspend fun get(injector: AsyncInjector): T = instance
    override suspend fun deinit() {
        (instance as? AsyncDestructor?)?.deinit()
    }
    override fun toString(): String = "InstanceAsyncObjectProvider($instance)"
}

class AsyncInjector(val parent: AsyncInjector? = null, val level: Int = 0) {
    companion object {}
    suspend inline fun  getWith(vararg instances: Any): T = getWith(T::class, *instances)
    suspend inline fun  get(): T = get(T::class)
    suspend inline fun  getOrNull(): T? = getOrNull(T::class)
    inline fun  mapInstance(instance: T): AsyncInjector = mapInstance(T::class, instance)
    inline fun  mapFactory(noinline gen: suspend AsyncInjector.() -> AsyncFactory) =
        mapFactory(T::class, gen)

    inline fun  mapSingleton(noinline gen: suspend AsyncInjector.() -> T) = mapSingleton(T::class, gen)
    inline fun  mapPrototype(noinline gen: suspend AsyncInjector.() -> T) = mapPrototype(T::class, gen)

    fun removeMapping(clazz: KClass<*>) {
        providersByClass.remove(clazz)
        parent?.removeMapping(clazz)
    }

    var fallbackProvider: (suspend (clazz: kotlin.reflect.KClass<*>, ctx: RequestContext) -> AsyncObjectProvider<*>)? = null
    val providersByClass = LinkedHashMap, AsyncObjectProvider<*>>()

    val root: AsyncInjector = parent?.root ?: this
    val nearestFallbackProvider get() = fallbackProvider ?: parent?.fallbackProvider

    fun child() = AsyncInjector(this, level + 1)

    suspend fun  getWith(clazz: KClass, vararg instances: Any): T {
        val c = child()
        for (i in instances) {
            @Suppress("UNCHECKED_CAST")
            c.mapInstance(i::class as KClass, i)
        }
        return c.get(clazz)
    }

    fun dump() {
        println("$this")
        for ((k, v) in providersByClass) {
            println("- $k: $v")
        }
        parent?.dump()
    }

    fun  mapInstance(clazz: KClass, instance: T): AsyncInjector = this.apply {
        providersByClass[clazz] = InstanceAsyncObjectProvider(instance)
    }

    fun  mapFactory(clazz: KClass, gen: suspend AsyncInjector.() -> AsyncFactory): AsyncInjector =
        this.apply {
            providersByClass[clazz] = FactoryAsyncObjectProvider(gen)
        }

    fun  mapSingleton(clazz: KClass, gen: suspend AsyncInjector.() -> T): AsyncInjector = this.apply {
        providersByClass[clazz] = SingletonAsyncObjectProvider(gen)
    }

    fun  mapPrototype(clazz: KClass, gen: suspend AsyncInjector.() -> T): AsyncInjector = this.apply {
        providersByClass[clazz] = PrototypeAsyncObjectProvider(gen)
    }

    init {
        mapInstance(AsyncInjector::class, this)
    }

    data class RequestContext(val initialClazz: KClass<*>)

    fun getClassDefiner(clazz: KClass<*>): AsyncInjector? {
        if (clazz in providersByClass) return this
        return parent?.getClassDefiner(clazz)
    }

    suspend fun  getProviderOrNull(
        clazz: KClass,
        ctx: RequestContext = RequestContext(clazz)
    ): AsyncObjectProvider? {
        return (providersByClass[clazz]
            ?: parent?.getProviderOrNull(clazz, ctx)
            ?: nearestFallbackProvider?.invoke(clazz, ctx)?.also { providersByClass[clazz] = it }
        ) as? AsyncObjectProvider?
    }

    suspend fun  getProvider(
        clazz: KClass,
        ctx: RequestContext = RequestContext(clazz)
    ): AsyncObjectProvider =
        getProviderOrNull(clazz, ctx) ?: throw AsyncInjector.NotMappedException(
            clazz, ctx.initialClazz, ctx, "Class '$clazz' doesn't have constructors $ctx"
        )

    @Suppress("UNCHECKED_CAST")
    suspend fun  getOrNull(clazz: KClass, ctx: RequestContext = RequestContext(clazz)): T? {
        return getProviderOrNull(clazz, ctx)?.get(this)
    }

    inline fun  getSync(ctx: RequestContext = RequestContext(T::class)): T = getSync(T::class, ctx)
    inline fun  getSyncOrNull(ctx: RequestContext = RequestContext(T::class)): T? = getSyncOrNull(T::class, ctx)
    fun  getSync(clazz: KClass, ctx: RequestContext = RequestContext(clazz)): T {
        return getSyncOrNull(clazz, ctx) ?: throw RuntimeException("Couldn't get instance of type $clazz synchronously")
    }
    fun  getSyncOrNull(clazz: KClass, ctx: RequestContext = RequestContext(clazz)): T? {
        var rresult: T? = null
        var rexception: Throwable? = null
        suspend {
            getOrNull(clazz, ctx)
        }.startCoroutine(object : Continuation {
            override val context: CoroutineContext = EmptyCoroutineContext

            override fun resumeWith(result: Result) {
                val exception = result.exceptionOrNull()
                if (exception != null) {
                    rexception = exception
                } else {
                    rresult = result.getOrNull()
                }
            }
        })
        if (rexception != null) throw rexception!!
        try {
            return rresult
        } catch (e: Throwable) {
            if (e is CancellationException) throw e
            throw RuntimeException("Couldn't get instance of type $clazz synchronously", e)
        }
    }

    @Suppress("UNCHECKED_CAST")
    suspend fun  get(clazz: KClass, ctx: RequestContext = RequestContext(clazz)): T {
        return getProvider(clazz, ctx).get(this)
    }

    suspend fun  has(clazz: KClass): Boolean = getProviderOrNull(clazz) != null

    class NotMappedException(
        val clazz: KClass<*>,
        val requestedByClass: KClass<*>,
        val ctx: RequestContext,
        val msg: String = "Not mapped $clazz requested by $requestedByClass in $ctx"
    ) : RuntimeException(msg)

    override fun toString(): String = "AsyncInjector(level=$level)"

    suspend internal fun  created(instance: T): T {
        if (instance is AsyncDependency) instance.init()
        if (instance is InjectorAsyncDependency) instance.init(this)
        if (instance is AsyncDestructor) deinitList.add(instance)
        return instance
    }

    private val deinitList = arrayListOf()

    fun addDeinit(value: AsyncDestructor) {
        deinitList.add(value)
    }

    suspend fun deinit() {
        for (pair in providersByClass) pair.value.deinit()
        for (deinit in deinitList) deinit.deinit()
        deinitList.clear()
    }
}

interface AsyncFactory {
    suspend fun create(): T
}

interface InjectedHandler {
    suspend fun injectedInto(instance: Any): Unit
}

annotation class AsyncFactoryClass(val clazz: KClass>)
//annotation class AsyncFactoryClass(val clazz: KClass>)
//annotation class AsyncFactoryClass(val clazz: KClass>)
//annotation class AsyncFactoryClass(val clazz: kotlin.reflect.KClass>)

interface AsyncDependency {
    suspend fun init(): Unit
}

interface AsyncDestructor {
    suspend fun deinit(): Unit
}

interface InjectorAsyncDependency {
    suspend fun init(injector: AsyncInjector): Unit
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy