@file:Suppress("MemberVisibilityCanBePrivate") package net.axay.kspigot.runnables import kotlin.reflect.KClass abstract class ChainedRunnablePart( val sync: Boolean ) { var next: ChainedRunnablePart? = null protected abstract fun invoke(data: T): R /** * Begins execution of this chained runnable. */ abstract fun execute() /** * Begins execution of this chained runnable, catching any exception of * type [E] and passing it to the optional [exceptionHandler]. * * @param exceptionSync whether the exception handler should be executed * synchronously or asynchronously, defaults to `true` (Note that usage of * any Spigot API functions requires it to be sync) */ inline fun executeCatching( exceptionSync: Boolean = true, noinline exceptionHandler: ((E) -> Unit)? = null ) { executeCatchingImpl(E::class, exceptionSync, exceptionHandler) } /** * Has to be public for use in inline function [executeCatching], not * intended to be used directly. */ abstract fun executeCatchingImpl( exceptionClass: KClass, exceptionSync: Boolean, exceptionHandler: ((E) -> Unit)?, ) protected fun start(data: T) { taskRun(sync) { val result = invoke(data) next?.start(result) } } protected fun startCatching( data: T, exceptionClass: KClass, exceptionSync: Boolean, exceptionHandler: ((E) -> Unit)?, ) { taskRun(sync) { val result = try { invoke(data) } catch (e: Exception) { if (exceptionClass.isInstance(e)) { @Suppress("UNCHECKED_CAST") if (sync == exceptionSync) { exceptionHandler?.invoke(e as E) } else if (exceptionHandler != null) { taskRun(exceptionSync) { exceptionHandler.invoke(e as E) } } return@taskRun } else throw e } next?.startCatching(result, exceptionClass, exceptionSync, exceptionHandler) } } } class ChainedRunnablePartFirst( val runnable: () -> R, sync: Boolean ) : ChainedRunnablePart(sync) { override fun execute() = start(Unit) override fun executeCatchingImpl( exceptionClass: KClass, exceptionSync: Boolean, exceptionHandler: ((E) -> Unit)? ) = startCatching(Unit, exceptionClass, exceptionSync, exceptionHandler) override fun invoke(data: Unit) = runnable.invoke() } class ChainedRunnablePartThen( val runnable: (T) -> R, sync: Boolean, val previous: ChainedRunnablePart<*, T> ) : ChainedRunnablePart(sync) { override fun execute() = previous.execute() override fun executeCatchingImpl( exceptionClass: KClass, exceptionSync: Boolean, exceptionHandler: ((E) -> Unit)? ) = previous.executeCatchingImpl(exceptionClass, exceptionSync, exceptionHandler) override fun invoke(data: T) = runnable.invoke(data) } // FIRST fun firstDo(sync: Boolean, runnable: () -> R) = ChainedRunnablePartFirst(runnable, sync) fun firstSync(runnable: () -> R) = firstDo(true, runnable) fun firstAsync(runnable: () -> R) = firstDo(false, runnable) // THEN fun ChainedRunnablePart.thenDo(sync: Boolean, runnable: (R) -> U) = ChainedRunnablePartThen(runnable, sync, this).apply { previous.next = this } fun ChainedRunnablePart.thenSync(runnable: (R) -> U) = thenDo(true, runnable) fun ChainedRunnablePart.thenAsync(runnable: (R) -> U) = thenDo(false, runnable)