package dev.isxander.yacl3.dsl import dev.isxander.yacl3.api.Option import kotlin.properties.ReadOnlyProperty import kotlin.reflect.KProperty interface FutureValue { fun onReady(block: (T) -> Unit) fun map(block: (T) -> R): FutureValue fun flatMap(block: (T) -> FutureValue): FutureValue fun getOrNull(): T? fun getOrThrow(): T = getOrNull() ?: error("Value not ready yet!") open class Impl(default: T? = null) : FutureValue { var value: T? = default set(value) { field = value while (taskQueue.isNotEmpty()) { taskQueue.removeFirst()(value!!) } } private val taskQueue = ArrayDeque<(T) -> Unit>() override fun onReady(block: (T) -> Unit) { if (value != null) block(value!!) else taskQueue.add(block) } override fun map(block: (T) -> R): FutureValue { val future = Impl() onReady { future.value = block(it) } return future } override fun flatMap(block: (T) -> FutureValue): FutureValue { val future = Impl() onReady { block(it).onReady { inner -> future.value = inner } } return future } override fun getOrNull(): T? = value } } interface Reference : ReadOnlyProperty> { operator fun get(id: String): FutureValue override fun getValue(thisRef: Any?, property: KProperty<*>): FutureValue { return get(property.name) } operator fun invoke(name: String? = null, block: (T) -> Unit): ReadOnlyProperty> { return ReadOnlyProperty { thisRef, property -> val future = get(name ?: property.name) future.onReady(block) future } } } operator fun FutureValue>.get(id: String): FutureValue { val future = FutureValue.Impl>() onReady { future.value = it[id] } return future.flatten() } fun FutureValue.getOption(id: String): FutureValue> { val future = FutureValue.Impl>>() onReady { future.value = it.get(id) as FutureValue> } return future.flatten() } private fun FutureValue>.flatten(): FutureValue { val future = FutureValue.Impl() onReady { outer -> outer.onReady { inner -> future.value = inner } } return future } class RegisterableDelegateProvider( private val registerFunction: (String, Dsl.() -> Unit) -> Return, private val action: Dsl.() -> Unit ) { operator fun provideDelegate(thisRef: Any?, property: KProperty<*>): ExistingDelegateProvider { return ExistingDelegateProvider(registerFunction(property.name, action)) } } class ExistingDelegateProvider( private val delegate: Return ) { operator fun getValue(thisRef: Any?, property: KProperty<*>): Return { return delegate } }