All Downloads are FREE. Search and download functionalities are using the official Maven repository.
Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
commonMain.com.episode6.mockspresso2.internal.MxoInstance.kt Maven / Gradle / Ivy
package com.episode6.mockspresso2.internal
import com.episode6.mockspresso2.InvalidTypeReturnedFromMakerError
import com.episode6.mockspresso2.MockspressoInstance
import com.episode6.mockspresso2.api.Dependencies
import com.episode6.mockspresso2.api.DynamicObjectMaker
import com.episode6.mockspresso2.api.FallbackObjectMaker
import com.episode6.mockspresso2.api.RealObjectMaker
import com.episode6.mockspresso2.internal.util.DependencyCacheBuilder
import com.episode6.mockspresso2.internal.util.DependencyValidator
import com.episode6.mockspresso2.internal.util.RealObjectRequestsList
import com.episode6.mockspresso2.internal.util.runOnce
import com.episode6.mockspresso2.reflect.DependencyKey
import com.episode6.mockspresso2.reflect.asKClass
internal class MxoInstance(
private val parent: MxoInstance? = null,
val realMaker: RealObjectMaker,
val fallbackMaker: FallbackObjectMaker,
private val dynamicMakers: List,
setupCallbacks: MutableList<(MockspressoInstance) -> Unit>,
private val teardownCallbacks: List<() -> Unit>,
dependenciesBuilder: DependencyCacheBuilder,
private val realObjectRequests: RealObjectRequestsList,
) {
private val dependencies = dependenciesBuilder.build { asDependencies() }
val ensureInit: () -> Unit by runOnce {
parent?.ensureInit?.invoke()
// warm real objects cache
realObjectRequests.forEach { key ->
if (!dependencies.containsKey(key)) {
createInternal(key, DependencyValidator(key), cache = true)
}
}
// fire setup callbacks
val container = MockspressoInstanceContainer(this)
setupCallbacks.forEach { it.invoke(container) }
}
fun get(key: DependencyKey): T {
ensureInit()
return getInternal(key, DependencyValidator(key))
}
fun create(key: DependencyKey): T {
ensureInit()
return createInternal(key, DependencyValidator(key), cache = false)
}
fun teardown() {
ensureInit()
teardownCallbacks.forEach { it.invoke() }
parent?.teardown()
}
private fun canGetInternal(key: DependencyKey, validator: DependencyValidator): TypedObjectAnswer {
if (dependencies.containsKey(key)) return Yes(dependencies.get(key, validator))
if (realObjectRequests.containsKey(key)) return Yes(createInternal(key, validator, cache = true))
val isSpecial = dynamicMakers.canMake(key, validator.asDependencies())
if (isSpecial is DynamicObjectMaker.Answer.Yes) return isSpecial.castAndCache(key, validator)
return parent?.canGetInternal(key, validator) ?: No
}
private fun getInternal(key: DependencyKey, validator: DependencyValidator): T {
return when (val got = canGetInternal(key, validator)) {
is Yes -> got.value
is No -> fallbackMaker.makeObject(key).cacheWith(key, validator)
}
}
private fun createInternal(key: DependencyKey, validator: DependencyValidator, cache: Boolean): T =
realMaker
.makeRealObject(realObjectRequests.getImplFor(key), validator.asDependencies())
.checkedCast(key)
.let { realObjectRequests.intercept(key, it) }
.also { if (cache) it.cacheWith(key, validator) }
private fun DependencyValidator.asDependencies(): Dependencies = object : Dependencies {
override fun get(key: DependencyKey): T = getInternal(key, childValidator(key))
}
private fun T.cacheWith(key: DependencyKey, validator: DependencyValidator): T =
also { dependencies.put(key, validator) { it } }
private fun DynamicObjectMaker.Answer.Yes.castAndCache(
key: DependencyKey,
validator: DependencyValidator
): Yes =
Yes(value.checkedCast(key).cacheWith(key, validator))
}
private fun List.canMake(
key: DependencyKey<*>,
dependencies: Dependencies
): DynamicObjectMaker.Answer = firstNotNullOfOrNull { maker ->
maker.canMakeObject(key, dependencies).takeIf { it is DynamicObjectMaker.Answer.Yes }
} ?: DynamicObjectMaker.Answer.No
private sealed class TypedObjectAnswer
private data class Yes(val value: T) : TypedObjectAnswer()
private object No : TypedObjectAnswer()
@Suppress("UNCHECKED_CAST")
private fun Any?.checkedCast(key: DependencyKey): T {
if (!key.token.asKClass().isInstance(this)) {
throw InvalidTypeReturnedFromMakerError("Tried to create $key and got un-castable result: $this")
}
return this as T
}