Skip to content

Instantly share code, notes, and snippets.

@happy-bracket
Created October 30, 2020 14:04
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save happy-bracket/8626f5a387d51c289097ea9fd3758934 to your computer and use it in GitHub Desktop.
Save happy-bracket/8626f5a387d51c289097ea9fd3758934 to your computer and use it in GitHub Desktop.
interface Traced<E, S : Trace.Stage> {
val trace: Trace<E, S>
}
data class EffectTracer<E : Traced<E, Trace.Stage.Out>, M : Traced<E, Trace.Stage.In>> internal constructor(
private val state: State,
private val factory: (Trace<E, Trace.Stage.Out>) -> E
) {
fun launch(): Pair<EffectTracer<E, M>, Set<E>> {
val trace = Trace<E, Trace.Stage.Out>(if (state is State.Running) Restart else Launch)
val effect = factory(trace)
return this to setOf(effect)
}
fun cancel(): Pair<EffectTracer<E, M>, Set<E>> {
if (state is State.Idle) return this to emptySet()
val newTracer = copy(state = State.Idle)
val trace = Trace<E, Trace.Stage.Out>(Cancel)
return newTracer to setOf(factory(trace))
}
fun checkResponse(tracedMutation: M): EffectTracer<E, M> {
return when (tracedMutation.trace.stage) {
is Finished -> copy(state = State.Idle)
is HoldOn -> copy(state = State.Running)
}
}
fun isIdle(): Boolean = state is State.Idle
sealed class State {
object Running : State()
object Idle : State()
}
companion object {
operator fun <E : Traced<E, Trace.Stage.Out>, M : Traced<E, Trace.Stage.In>> invoke(factory: (Trace<E, Trace.Stage.Out>) -> E): EffectTracer<E, M> {
return EffectTracer(State.Idle, factory)
}
}
}
data class Trace<Tag, S : Trace.Stage> internal constructor(val stage: S) {
sealed class Stage {
sealed class Out : Stage() {
object Launch : Out()
object Restart : Out()
object Cancel : Out()
}
sealed class In : Stage() {
object Finished : In()
object HoldOn : In()
}
}
}
fun <E> Trace<E, Trace.Stage.Out>.nextStage(isFinished: Boolean): Trace<E, Trace.Stage.In> = Trace(if (isFinished) Finished else HoldOn)
class TraceSubHandler<E : Traced<E, Trace.Stage.Out>, M : Traced<E, Trace.Stage.In>>(private val executingHandler: Handler<E, M>) {
private val semaphore: Channel<Red> = Channel()
fun handle(effect: E): Flow<M> {
semaphore.consumeAsFlow()
semaphore.offer(Red)
return if (effect.trace.stage is Cancel) {
emptyFlow()
} else {
val originalFlow = executingHandler.handle(effect)
return merge(semaphore.receiveAsFlow(), originalFlow.map { Green(it) })
.takeWhile { it !is Red }
.transform { if (it is Green) emit(it.value) }
}
}
private sealed class Semaphore<out T> {
class Green<T>(val value: T) : Semaphore<T>()
object Red : Semaphore<Nothing>()
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment