diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..a78b971 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,23 @@ +# Builder stage +FROM gradle:jdk17-noble AS builder + +# Set working directory +WORKDIR /app + +# Copy the project files +COPY . . + +# Generate Dokka documentation +RUN gradle --no-daemon :hakate:dokkaHtml + +# Final stage +FROM nginx:alpine + +# Copy Dokka documentation from builder stage to Nginx HTML directory +COPY --from=builder /app/hakate/build/dokka/html/ /usr/share/nginx/html/ + +# Expose port 80 +EXPOSE 80 + +# Start Nginaaa +CMD ["nginx", "-g", "daemon off;"] diff --git a/convention-plugins/src/main/kotlin/root.publication.gradle.kts b/convention-plugins/src/main/kotlin/root.publication.gradle.kts index b23201c..667418b 100644 --- a/convention-plugins/src/main/kotlin/root.publication.gradle.kts +++ b/convention-plugins/src/main/kotlin/root.publication.gradle.kts @@ -1,9 +1,24 @@ +/** + * Root publication configuration script for the project. + * This script sets up the necessary configuration for publishing artifacts to Maven Central. + */ plugins { id("io.github.gradle-nexus.publish-plugin") } + +/** + * Configuration object containing project-wide constants. + */ object Conf { + /** + * The group ID used for all artifacts in this project. + */ const val GROUP = "net.kigawa" - const val VERSION = "3.3.2" + + /** + * The version number for all artifacts in this project. + */ + const val VERSION = "3.4.0" } group = Conf.GROUP 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 7d18e8b..403c43f 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 @@ -3,21 +3,92 @@ package net.kigawa.hakate.api.state import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Job import net.kigawa.hakate.impl.state.MergedStateContextImpl +import kotlin.coroutines.CoroutineContext +import kotlin.coroutines.EmptyCoroutineContext +/** + * Interface for managing state and coroutine operations in a structured way. + * Provides methods for launching coroutines, dispatching operations, and collecting state changes. + */ interface StateContext { + /** + * The coroutine scope associated with this state context. + * Used for launching coroutines and managing their lifecycle. + */ val coroutineScope: CoroutineScope + + /** + * Launches a new coroutine within the context's scope. + * + * @param block The suspend function to execute in the coroutine. + * @return A [Job] that can be used to control the launched coroutine. + */ fun launch(block: suspend CoroutineScope.() -> Unit): Job + + /** + * Creates a new state dispatcher associated with this context. + * + * @return A new [StateDispatcher] instance. + */ fun dispatcher(): StateDispatcher + + /** + * Dispatches a block of code to be executed in a new state context. + * + * @param block The suspend function to execute in the new context. + * @return A [Job] that can be used to control the launched coroutine. + */ fun dispatch(block: suspend StateContext.() -> Unit): Job + + /** + * Extension function to collect state changes with an accumulator. + * + * @param T The type of the state value. + * @param R The type of the accumulated result. + * @param defaultValue The initial value for the accumulator. + * @param block The function to execute for each state change, receiving the new value and previous accumulated result. + * @return The result of the collection operation. + */ fun State.collect(defaultValue: R, block: suspend StateContext.(value: T, prev: R) -> R) = this.collect(this@StateContext, defaultValue, block) + /** + * Extension function to collect state changes. + * + * @param T The type of the state value. + * @param block The function to execute for each state change. + * @return The result of the collection operation. + */ fun State.collect(block: suspend StateContext.(value: T) -> Unit) = this.collect(this@StateContext, block) + /** + * Cancels all coroutines associated with this context. + */ fun cancel() + + /** + * Merges this context with another context. + * + * @param other The context to merge with. + * @return A new [StateContext] that combines the behavior of both contexts. + */ fun merge(other: StateContext): StateContext { return MergedStateContextImpl(this, other) } - fun newStateContext(): StateContext -} \ No newline at end of file + + /** + * Creates a new state context with the specified coroutine context. + * + * @param coroutineContext The coroutine context to use for the new state context. + * @return A new [StateContext] with the specified coroutine context. + */ + fun withCoroutineContext(coroutineContext: CoroutineContext): StateContext + + /** + * Creates a new state context with an empty coroutine context. + * + * @return A new [StateContext] with an empty coroutine context. + */ + fun newStateContext(): StateContext = withCoroutineContext(EmptyCoroutineContext) +} 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 index baa64aa..031e7ff 100644 --- a/hakate/src/commonMain/kotlin/net/kigawa/hakate/impl/state/MergedStateContextImpl.kt +++ b/hakate/src/commonMain/kotlin/net/kigawa/hakate/impl/state/MergedStateContextImpl.kt @@ -4,16 +4,35 @@ import kotlinx.coroutines.* import net.kigawa.hakate.api.state.StateContext import net.kigawa.hakate.api.state.StateDispatcher import net.kigawa.hakate.impl.Utl.suspendApply +import kotlin.coroutines.CoroutineContext import kotlin.coroutines.EmptyCoroutineContext +/** + * Implementation of [StateContext] that merges the behavior of two state contexts. + * Operations are typically performed on both contexts, with results combined appropriately. + * + * @property first The primary state context. + * @property second The secondary state context to merge with the primary one. + */ class MergedStateContextImpl( private val first: StateContext, private val second: StateContext, ) : StateContext { + /** + * The merged coroutine scope that combines contexts from both state contexts. + * Uses the first context as a base and adds the second context's coroutine context. + */ override val coroutineScope: CoroutineScope = CoroutineScope( first.coroutineScope.newCoroutineContext(second.coroutineScope.coroutineContext) ) + /** + * Launches a new coroutine that coordinates execution across both contexts. + * The block is executed in a context that combines both state contexts' coroutine contexts. + * + * @param block The suspend function to execute in the coroutine. + * @return A [Job] that can be used to control the launched coroutine. + */ override fun launch(block: suspend CoroutineScope.() -> Unit): Job { return first.launch { val f = this @@ -24,7 +43,21 @@ class MergedStateContextImpl( } } + /** + * Creates a new state dispatcher associated with this context. + * Uses the first context's dispatcher implementation. + * + * @return A [StateDispatcher] from the first context. + */ override fun dispatcher(): StateDispatcher = first.dispatcher() + + /** + * Dispatches a block of code to be executed in a new state context. + * Creates a new [StateContextImpl] within the launched coroutine to execute the block. + * + * @param block The suspend function to execute in the new context. + * @return A [Job] that can be used to control the launched coroutine. + */ override fun dispatch( block: suspend StateContext.() -> Unit, ): Job { @@ -35,15 +68,27 @@ class MergedStateContextImpl( } } + /** + * Cancels all coroutines associated with both contexts. + * Calls cancel on both the first and second contexts. + */ override fun cancel() { first.cancel() second.cancel() } - override fun newStateContext(): StateContext { + /** + * Creates a new state context with the specified coroutine context. + * Returns a new [StateContextImpl] with a coroutine scope that combines the current scope's context + * with the provided coroutine context. + * + * @param coroutineContext The coroutine context to use for the new state context. + * @return A new [StateContext] with the specified coroutine context. + */ + override fun withCoroutineContext(coroutineContext: CoroutineContext): StateContext { return StateContextImpl( dispatcher(), - CoroutineScope(coroutineScope.newCoroutineContext(EmptyCoroutineContext)) + CoroutineScope(coroutineScope.newCoroutineContext(coroutineContext)) ) } -} \ 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 e888fbc..6a93237 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 @@ -5,17 +5,46 @@ import net.kigawa.hakate.api.state.StateContext import net.kigawa.hakate.api.state.StateDispatcher import net.kigawa.hakate.impl.Utl.suspendApply import net.kigawa.hakate.impl.dispatcher.StateDispatcherImpl +import kotlin.coroutines.CoroutineContext import kotlin.coroutines.EmptyCoroutineContext +/** + * Standard implementation of the [StateContext] interface. + * Manages coroutine operations and state dispatching. + * + * @property dispatcher The state dispatcher used for handling state operations. + * @property coroutineScope The coroutine scope associated with this context. + */ class StateContextImpl( private val dispatcher: StateDispatcher, override val coroutineScope: CoroutineScope, ) : StateContext { + /** + * Launches a new coroutine within this context's scope. + * Delegates to the underlying coroutineScope's launch function. + * + * @param block The suspend function to execute in the coroutine. + * @return A [Job] that can be used to control the launched coroutine. + */ override fun launch(block: suspend CoroutineScope.() -> Unit): Job { return coroutineScope.launch(block = block) } + /** + * Creates a new state dispatcher associated with this context. + * Returns a new instance of [StateDispatcherImpl] with the current coroutine scope. + * + * @return A new [StateDispatcher] instance. + */ override fun dispatcher(): StateDispatcher = StateDispatcherImpl(coroutineScope) + + /** + * Dispatches a block of code to be executed in a new state context. + * Creates a new [StateContextImpl] within the launched coroutine to execute the block. + * + * @param block The suspend function to execute in the new context. + * @return A [Job] that can be used to control the launched coroutine. + */ override fun dispatch( block: suspend StateContext.() -> Unit, ): Job { @@ -26,11 +55,23 @@ class StateContextImpl( } } + /** + * Cancels all coroutines associated with this context. + * Cancels the underlying coroutine scope with a specific cancellation message. + */ override fun cancel() { coroutineScope.cancel("cancel by StateContext") } - override fun newStateContext(): StateContext { - return StateContextImpl(dispatcher, CoroutineScope(coroutineScope.newCoroutineContext(EmptyCoroutineContext))) + /** + * Creates a new state context with the specified coroutine context. + * Returns a new [StateContextImpl] with a coroutine scope that combines the current scope's context + * with the provided coroutine context. + * + * @param coroutineContext The coroutine context to use for the new state context. + * @return A new [StateContext] with the specified coroutine context. + */ + override fun withCoroutineContext(coroutineContext: CoroutineContext): StateContext { + return StateContextImpl(dispatcher, CoroutineScope(coroutineScope.newCoroutineContext(coroutineContext))) } -} \ No newline at end of file +}