diff --git a/convention-plugins/src/main/kotlin/root.publication.gradle.kts b/convention-plugins/src/main/kotlin/root.publication.gradle.kts index 4a12397..afad449 100644 --- a/convention-plugins/src/main/kotlin/root.publication.gradle.kts +++ b/convention-plugins/src/main/kotlin/root.publication.gradle.kts @@ -3,7 +3,7 @@ plugins { } object Conf { const val GROUP = "net.kigawa" - const val VERSION = "2.1.0" + const val VERSION = "3.1.0" } group = Conf.GROUP diff --git a/hakate/src/commonMain/kotlin/net/kigawa/hakate/api/multi/MultiValue2.kt b/hakate/src/commonMain/kotlin/net/kigawa/hakate/api/multi/MultiValue2.kt new file mode 100644 index 0000000..2e84958 --- /dev/null +++ b/hakate/src/commonMain/kotlin/net/kigawa/hakate/api/multi/MultiValue2.kt @@ -0,0 +1,9 @@ +package net.kigawa.hakate.api.multi + +import net.kigawa.hakate.api.state.State + +fun mergeState(state1: State, state2: State): State> { + return state1.merge(state2) { a, b -> MultiValue2(a, b) } +} + +data class MultiValue2(val first: T, val second: U) \ No newline at end of file diff --git a/hakate/src/commonMain/kotlin/net/kigawa/hakate/api/multi/MultiValue3.kt b/hakate/src/commonMain/kotlin/net/kigawa/hakate/api/multi/MultiValue3.kt new file mode 100644 index 0000000..09b72de --- /dev/null +++ b/hakate/src/commonMain/kotlin/net/kigawa/hakate/api/multi/MultiValue3.kt @@ -0,0 +1,14 @@ +package net.kigawa.hakate.api.multi + +import net.kigawa.hakate.api.state.State + +@Suppress("unused") +fun mergeState(state1: State, state2: State, state3: State): State> { + return mergeState(state1, state2).merge(state3) +} + +fun State>.merge(state3: State): State> { + return merge(state3) { a, b -> MultiValue3(a.first, a.second, b) } +} + +data class MultiValue3(val first: T, val second: U, val third: V) \ No newline at end of file diff --git a/hakate/src/commonMain/kotlin/net/kigawa/hakate/api/state/State.kt b/hakate/src/commonMain/kotlin/net/kigawa/hakate/api/state/State.kt index 540a4ee..40c1067 100644 --- a/hakate/src/commonMain/kotlin/net/kigawa/hakate/api/state/State.kt +++ b/hakate/src/commonMain/kotlin/net/kigawa/hakate/api/state/State.kt @@ -2,7 +2,9 @@ package net.kigawa.hakate.api.state import kotlinx.coroutines.Job +@Suppress("unused") interface State { + val stateContext: StateContext fun collect(context: StateContext, block: suspend StateContext.(value: T) -> Unit): Job fun collect( context: StateContext, defaultValue: R, block: suspend StateContext.(value: T, prev: R) -> R, @@ -20,4 +22,5 @@ interface State { fun child(block: (T) -> R): State fun currentValue(): T + fun merge(state: State, block: (first: T, second: U) -> R): State } diff --git a/hakate/src/commonMain/kotlin/net/kigawa/hakate/api/state/StateContext.kt b/hakate/src/commonMain/kotlin/net/kigawa/hakate/api/state/StateContext.kt index aa8a172..fce27e4 100644 --- a/hakate/src/commonMain/kotlin/net/kigawa/hakate/api/state/StateContext.kt +++ b/hakate/src/commonMain/kotlin/net/kigawa/hakate/api/state/StateContext.kt @@ -2,9 +2,10 @@ package net.kigawa.hakate.api.state import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Job +import net.kigawa.hakate.impl.state.MergedStateContextImpl interface StateContext { - val coroutineScope: CoroutineScope + fun launch(block: suspend CoroutineScope.() -> Unit): Job fun dispatcher(): StateDispatcher fun dispatch(block: suspend StateContext.() -> Unit): Job fun State.collect(defaultValue: R, block: suspend StateContext.(value: T, prev: R) -> R) = @@ -14,4 +15,7 @@ interface StateContext { this.collect(this@StateContext, block) fun cancel() + fun merge(other: StateContext): StateContext { + return MergedStateContextImpl(this, other) + } } \ No newline at end of file diff --git a/hakate/src/commonMain/kotlin/net/kigawa/hakate/impl/state/MergedStateContextImpl.kt b/hakate/src/commonMain/kotlin/net/kigawa/hakate/impl/state/MergedStateContextImpl.kt new file mode 100644 index 0000000..88ba43c --- /dev/null +++ b/hakate/src/commonMain/kotlin/net/kigawa/hakate/impl/state/MergedStateContextImpl.kt @@ -0,0 +1,40 @@ +package net.kigawa.hakate.impl.state + +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Job +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext +import net.kigawa.hakate.api.state.StateContext +import net.kigawa.hakate.api.state.StateDispatcher +import net.kigawa.hakate.impl.Utl.suspendApply + +class MergedStateContextImpl( + private val first: StateContext, + private val second: StateContext, +) : StateContext { + override fun launch(block: suspend CoroutineScope.() -> Unit): Job { + return first.launch { + val f = this + second.launch { + val s = this + f.launch { withContext(s.coroutineContext) { block() } } + } + } + } + + override fun dispatcher(): StateDispatcher = first.dispatcher() + override fun dispatch( + block: suspend StateContext.() -> Unit, + ): Job { + return launch { + StateContextImpl(dispatcher(), this@launch).suspendApply { + block() + } + } + } + + override fun cancel() { + first.cancel() + second.cancel() + } +} \ No newline at end of file diff --git a/hakate/src/commonMain/kotlin/net/kigawa/hakate/impl/state/StateContextImpl.kt b/hakate/src/commonMain/kotlin/net/kigawa/hakate/impl/state/StateContextImpl.kt index 01e6d27..99d7ee3 100644 --- a/hakate/src/commonMain/kotlin/net/kigawa/hakate/impl/state/StateContextImpl.kt +++ b/hakate/src/commonMain/kotlin/net/kigawa/hakate/impl/state/StateContextImpl.kt @@ -10,13 +10,17 @@ import net.kigawa.hakate.impl.Utl.suspendApply class StateContextImpl( private val dispatcher: StateDispatcher, - override val coroutineScope: CoroutineScope, + val coroutineScope: CoroutineScope, ) : StateContext { + override fun launch(block: suspend CoroutineScope.() -> Unit): Job { + return coroutineScope.launch(block = block) + } + override fun dispatcher(): StateDispatcher = dispatcher override fun dispatch( block: suspend StateContext.() -> Unit, ): Job { - return coroutineScope.launch { + return launch { StateContextImpl(dispatcher, this@launch).suspendApply { block() } diff --git a/hakate/src/commonMain/kotlin/net/kigawa/hakate/impl/state/StateImpl.kt b/hakate/src/commonMain/kotlin/net/kigawa/hakate/impl/state/StateImpl.kt index c134509..bbbcfcc 100644 --- a/hakate/src/commonMain/kotlin/net/kigawa/hakate/impl/state/StateImpl.kt +++ b/hakate/src/commonMain/kotlin/net/kigawa/hakate/impl/state/StateImpl.kt @@ -8,7 +8,7 @@ import net.kigawa.hakate.api.state.StateContext class StateImpl( defaultValue: T, - private val stateContext: StateContext, + override val stateContext: StateContext, ) : MutableState { private val flow = MutableStateFlow(defaultValue) override fun collect( @@ -45,5 +45,17 @@ class StateImpl( } override fun currentValue(): T = flow.value + override fun merge( + state: State, block: (T, U) -> R, + ): State { + val mergedContext = stateContext.merge(state.stateContext) + val newState = StateImpl(block(currentValue(), state.currentValue()), mergedContext) + collect(mergedContext) { v1 -> + state.collect(mergedContext) { v2 -> + newState.set(block(v1, v2)) + } + } + return newState + } } \ No newline at end of file diff --git a/hakate/src/commonTest/kotlin/net/kigawa/hakate/api/state/StateTest.kt b/hakate/src/commonTest/kotlin/net/kigawa/hakate/api/state/StateTest.kt index 65d46ac..735de63 100644 --- a/hakate/src/commonTest/kotlin/net/kigawa/hakate/api/state/StateTest.kt +++ b/hakate/src/commonTest/kotlin/net/kigawa/hakate/api/state/StateTest.kt @@ -54,8 +54,8 @@ class StateTest : StateTestBase() { isSet = false state.set(next) -// waitStateSet { isSet } -// assertEquals("$next-child", value) + waitStateSet { isSet } + assertEquals("$next-child", value) } }