Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -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;"]
17 changes: 16 additions & 1 deletion convention-plugins/src/main/kotlin/root.publication.gradle.kts
Original file line number Diff line number Diff line change
@@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 <T, R> State<T>.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 <T> State<T>.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
}

/**
* 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)
}
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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 {
Expand All @@ -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))
)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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)))
}
}
}
Loading