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

commonMain.org.kodein.di.bindings.set.kt Maven / Gradle / Ivy

There is a newer version: 7.22.0
Show newest version
package org.kodein.di.bindings

import org.kodein.di.*
import org.kodein.di.internal.DIBuilderImpl
import org.kodein.type.TypeToken

/**
 * Base class for binding set.
 *
 * @param C The context type of all bindings in the set.
 * @param A The argument type of all bindings in the set.
 * @param T The provided type of all bindings in the set.
 */
public abstract class BaseMultiBinding : DIBinding> {
    internal abstract val set: MutableSet>

    override fun factoryName(): String = "bindingSet"
}

private class SetBindingDI(private val _base: BindingDI) : BindingDI by _base {
    override fun overriddenFactory() = throw IllegalStateException("Cannot access overrides in a Set binding")
    override fun overriddenFactoryOrNull() = throw IllegalStateException("Cannot access overrides in a Set binding")
}


/**
 * Binding that holds multiple factory bindings (e.g. with argument) in a set.
 *
 * @param C The context type of all bindings in the set.
 * @param A The argument type of all bindings in the set.
 * @param T The provided type of all bindings in the set.
 */
public class ArgSetBinding(override val contextType: TypeToken, override val argType: TypeToken, private val _elementType: TypeToken, override val createdType: TypeToken>) : BaseMultiBinding() {

    override val set = LinkedHashSet>()

    override fun getFactory(key: DI.Key>, di: BindingDI): (A) -> Set {
        var lateInitFactories: List<(A) -> T>? = null
        return { arg ->
            val factories = lateInitFactories ?: run {
                val subKey = DI.Key(key.contextType, key.argType, _elementType, key.tag)
                set.map { it.getFactory(subKey, SetBindingDI(di)) }
            }.also { lateInitFactories = it }
            factories.asSequence().map { it.invoke(arg) } .toSet()
        }
    }

    override val copier: DIBinding.Copier> = DIBinding.Copier { builder ->
        ArgSetBinding(contextType, argType, _elementType, createdType).also {
            it.set.addAll(set.map { it.copier?.copy(builder) ?: it })
        }
    }
}

/**
 * Binding that holds multiple factory bindings (e.g. with argument) in a set.
 *
 * @param C The context type of all bindings in the set.
 * @param T The provided type of all bindings in the set.
 */
public class SetBinding(override val contextType: TypeToken, private val _elementType: TypeToken, override val createdType: TypeToken>) : NoArgDIBinding>, BaseMultiBinding() {

    @Suppress("UNCHECKED_CAST")
    override val set = LinkedHashSet>()

    override fun getFactory(key: DI.Key>, di: BindingDI): (Unit) -> Set {
        var lateInitProviders: List<(Unit) -> T>? = null
        return { _ ->
            val providers = lateInitProviders ?: run {
                val subKey = DI.Key(key.contextType, TypeToken.Unit, _elementType, key.tag)
                val subDI = SetBindingDI(di)
                set.map { it.getFactory(subKey, subDI) }
            }.also { lateInitProviders = it }
            providers.asSequence().map { it.invoke(Unit) }.toSet()
        }
    }

    override val copier: DIBinding.Copier> = DIBinding.Copier { builder ->
        SetBinding(contextType, _elementType, createdType).also {
            it.set.addAll(set.map { it.copier?.copy(builder) ?: it })
        }
    }
}

/**
 * Second part of the `bind().inSet() with binding` syntax.
 *
 * @param T The type of the binding in the set.
 */
public class TypeBinderInSet internal constructor(private val _binder: DI.Builder.TypeBinder, private val _colTypeToken: TypeToken) {

    /**
     * Second part of the `bind().inSet() with binding` syntax.
     *
     * @param C The context type of the binding.
     * @param binding The binding to add in the set.
     */
    @Suppress("UNCHECKED_CAST")
    public infix fun  with(binding: DIBinding) {
        _binder as DIBuilderImpl.TypeBinder
        val setKey = DI.Key(binding.contextType, binding.argType, _colTypeToken, _binder.tag)
        val setBinding = _binder.containerBuilder.bindingsMap[setKey]?.first() ?: throw IllegalStateException("No set binding to $setKey")

        setBinding.binding as? BaseMultiBinding ?: throw IllegalStateException("$setKey is associated to a ${setBinding.binding.factoryName()} while it should be associated with bindingSet")

        (setBinding.binding.set as MutableSet>).add(binding)
    }
}

/**
 * Allows to bind in an existing set binding (rather than directly as a new binding).
 *
 * First part of the `bind().inSet() with binding` syntax.
 *
 * @param T The provided type of all bindings in the set.
 * @param setTypeToken The type of the bound set.
 */
@Suppress("FunctionName")
public fun  DI.Builder.TypeBinder.InSet(setTypeToken: TypeToken>): TypeBinderInSet> = TypeBinderInSet(this, setTypeToken)




© 2015 - 2024 Weber Informatics LLC | Privacy Policy