Skip to content

Commit c067bf1

Browse files
committed
cloud-kotlin-coroutines (#318)
* Builder coroutine support * Move coroutines to version catalog * Add kdocs * Add note about simple coordinator * Update changelog
1 parent 094de15 commit c067bf1

10 files changed

Lines changed: 298 additions & 3 deletions

File tree

CHANGELOG.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77
## [1.6.0]
88

99
### Added
10-
- Kotlin: Support for suspending command functions using `AnnotationParser<C>.installCoroutineSupport()`
10+
- Kotlin: New module `cloud-kotlin-coroutines`: Support for suspending command handlers in builders and the Kotlin builder DSL
11+
- Kotlin: New module `cloud-kotlin-coroutines-annotations`: Support for suspending annotated command functions using
12+
`AnnotationParser<C>.installCoroutineSupport()`
1113
- Flags can be bound to a permission
1214
- Paper: Implement KeyedWorldArgument for matching worlds by their namespaced key
1315
- Annotations: Parser parameter annotations are now also parsed for flags ([#315](https://github.com/Incendo/cloud/pull/315))

cloud-kotlin/cloud-kotlin-coroutines-annotations/build.gradle.kts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,5 @@ dependencies {
66
api(project(":cloud-core"))
77
api(project(":cloud-annotations"))
88
api(kotlin("reflect"))
9-
api("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.2")
10-
api("org.jetbrains.kotlinx:kotlinx-coroutines-jdk8:1.5.2")
9+
api(libs.bundles.coroutines)
1110
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
plugins {
2+
id("cloud.kotlin-conventions")
3+
}
4+
5+
dependencies {
6+
api(project(":cloud-core"))
7+
api(libs.bundles.coroutines)
8+
9+
compileOnly(project(":cloud-kotlin-extensions"))
10+
testImplementation(project(":cloud-kotlin-extensions"))
11+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# Module cloud-kotlin-coroutines
2+
3+
Cloud extensions for Kotlin coroutine integration.
4+
5+
# Package cloud.commandframework.kotlin.coroutines
6+
7+
Cloud Kotlin coroutines classes and functions.
8+
9+
# Package cloud.commandframework.kotlin.coroutines.extension
10+
11+
Extension functions for existing Cloud types
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
//
2+
// MIT License
3+
//
4+
// Copyright (c) 2021 Alexander Söderberg & Contributors
5+
//
6+
// Permission is hereby granted, free of charge, to any person obtaining a copy
7+
// of this software and associated documentation files (the "Software"), to deal
8+
// in the Software without restriction, including without limitation the rights
9+
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10+
// copies of the Software, and to permit persons to whom the Software is
11+
// furnished to do so, subject to the following conditions:
12+
//
13+
// The above copyright notice and this permission notice shall be included in all
14+
// copies or substantial portions of the Software.
15+
//
16+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21+
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22+
// SOFTWARE.
23+
//
24+
package cloud.commandframework.kotlin.coroutines
25+
26+
import cloud.commandframework.context.CommandContext
27+
import cloud.commandframework.execution.AsynchronousCommandExecutionCoordinator
28+
import cloud.commandframework.execution.CommandExecutionCoordinator
29+
import cloud.commandframework.execution.CommandExecutionHandler
30+
import kotlinx.coroutines.CoroutineScope
31+
import kotlinx.coroutines.GlobalScope
32+
import kotlinx.coroutines.future.future
33+
import kotlin.coroutines.CoroutineContext
34+
import kotlin.coroutines.EmptyCoroutineContext
35+
36+
/**
37+
* Suspending version of [CommandExecutionHandler] for use with
38+
* coroutines.
39+
*
40+
* NOTE: It is highly advised to not use [CommandExecutionCoordinator.SimpleCoordinator] together
41+
* with coroutine support. Consider using [AsynchronousCommandExecutionCoordinator] instead.
42+
*
43+
* @param C command sender type
44+
*/
45+
public fun interface SuspendingExecutionHandler<C : Any> {
46+
/**
47+
* Handles command execution.
48+
*
49+
* @param commandContext command context
50+
*/
51+
public suspend operator fun invoke(commandContext: CommandContext<C>)
52+
53+
/**
54+
* Create a new [CommandExecutionHandler] for use in building commands,
55+
* backed by this [SuspendingExecutionHandler].
56+
*
57+
* @param scope coroutine scope
58+
* @param context coroutine context
59+
* @return new [CommandExecutionHandler]
60+
*/
61+
public fun asCommandExecutionHandler(
62+
scope: CoroutineScope = GlobalScope,
63+
context: CoroutineContext = EmptyCoroutineContext,
64+
): CommandExecutionHandler<C> = createCommandExecutionHandler(scope, context, this)
65+
66+
public companion object {
67+
/**
68+
* Create a new [CommandExecutionHandler] for use in building commands,
69+
* backed by the given [SuspendingExecutionHandler].
70+
*
71+
* @param scope coroutine scope
72+
* @param context coroutine context
73+
* @param handler suspending handler
74+
* @return new [CommandExecutionHandler]
75+
*/
76+
public fun <C : Any> createCommandExecutionHandler(
77+
scope: CoroutineScope = GlobalScope,
78+
context: CoroutineContext = EmptyCoroutineContext,
79+
handler: SuspendingExecutionHandler<C>,
80+
): CommandExecutionHandler<C> = CommandExecutionHandler.FutureCommandExecutionHandler { ctx ->
81+
scope.future(context) {
82+
handler(ctx)
83+
null
84+
}
85+
}
86+
}
87+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
//
2+
// MIT License
3+
//
4+
// Copyright (c) 2021 Alexander Söderberg & Contributors
5+
//
6+
// Permission is hereby granted, free of charge, to any person obtaining a copy
7+
// of this software and associated documentation files (the "Software"), to deal
8+
// in the Software without restriction, including without limitation the rights
9+
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10+
// copies of the Software, and to permit persons to whom the Software is
11+
// furnished to do so, subject to the following conditions:
12+
//
13+
// The above copyright notice and this permission notice shall be included in all
14+
// copies or substantial portions of the Software.
15+
//
16+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21+
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22+
// SOFTWARE.
23+
//
24+
package cloud.commandframework.kotlin.coroutines.extension
25+
26+
import cloud.commandframework.Command
27+
import cloud.commandframework.kotlin.coroutines.SuspendingExecutionHandler
28+
import kotlinx.coroutines.CoroutineScope
29+
import kotlinx.coroutines.GlobalScope
30+
import kotlin.coroutines.CoroutineContext
31+
import kotlin.coroutines.EmptyCoroutineContext
32+
33+
/**
34+
* Specify a suspending command execution handler.
35+
*
36+
* @param scope coroutine scope
37+
* @param context coroutine context
38+
* @param handler suspending handler
39+
* @return modified copy of this [Command.Builder]
40+
* @see Command.Builder.handler
41+
* @see SuspendingExecutionHandler
42+
*/
43+
public fun <C : Any> Command.Builder<C>.suspendingHandler(
44+
scope: CoroutineScope = GlobalScope,
45+
context: CoroutineContext = EmptyCoroutineContext,
46+
handler: SuspendingExecutionHandler<C>,
47+
): Command.Builder<C> = handler(SuspendingExecutionHandler.createCommandExecutionHandler(scope, context, handler))
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
//
2+
// MIT License
3+
//
4+
// Copyright (c) 2021 Alexander Söderberg & Contributors
5+
//
6+
// Permission is hereby granted, free of charge, to any person obtaining a copy
7+
// of this software and associated documentation files (the "Software"), to deal
8+
// in the Software without restriction, including without limitation the rights
9+
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10+
// copies of the Software, and to permit persons to whom the Software is
11+
// furnished to do so, subject to the following conditions:
12+
//
13+
// The above copyright notice and this permission notice shall be included in all
14+
// copies or substantial portions of the Software.
15+
//
16+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21+
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22+
// SOFTWARE.
23+
//
24+
package cloud.commandframework.kotlin.coroutines.extension
25+
26+
import cloud.commandframework.kotlin.MutableCommandBuilder
27+
import cloud.commandframework.kotlin.coroutines.SuspendingExecutionHandler
28+
import kotlinx.coroutines.CoroutineScope
29+
import kotlinx.coroutines.GlobalScope
30+
import kotlin.coroutines.CoroutineContext
31+
import kotlin.coroutines.EmptyCoroutineContext
32+
33+
/**
34+
* Specify a suspending command execution handler.
35+
*
36+
* @param scope coroutine scope
37+
* @param context coroutine context
38+
* @param handler suspending handler
39+
* @return this [MutableCommandBuilder]
40+
* @see MutableCommandBuilder.handler
41+
* @see SuspendingExecutionHandler
42+
*/
43+
public fun <C : Any> MutableCommandBuilder<C>.suspendingHandler(
44+
scope: CoroutineScope = GlobalScope,
45+
context: CoroutineContext = EmptyCoroutineContext,
46+
handler: SuspendingExecutionHandler<C>,
47+
): MutableCommandBuilder<C> = mutate {
48+
it.suspendingHandler(scope, context, handler)
49+
}
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
//
2+
// MIT License
3+
//
4+
// Copyright (c) 2021 Alexander Söderberg & Contributors
5+
//
6+
// Permission is hereby granted, free of charge, to any person obtaining a copy
7+
// of this software and associated documentation files (the "Software"), to deal
8+
// in the Software without restriction, including without limitation the rights
9+
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10+
// copies of the Software, and to permit persons to whom the Software is
11+
// furnished to do so, subject to the following conditions:
12+
//
13+
// The above copyright notice and this permission notice shall be included in all
14+
// copies or substantial portions of the Software.
15+
//
16+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21+
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22+
// SOFTWARE.
23+
//
24+
package cloud.commandframework.kotlin.coroutines
25+
26+
import cloud.commandframework.CommandManager
27+
import cloud.commandframework.execution.AsynchronousCommandExecutionCoordinator
28+
import cloud.commandframework.internal.CommandRegistrationHandler
29+
import cloud.commandframework.kotlin.coroutines.extension.suspendingHandler
30+
import cloud.commandframework.kotlin.extension.buildAndRegister
31+
import cloud.commandframework.meta.CommandMeta
32+
import cloud.commandframework.meta.SimpleCommandMeta
33+
import kotlinx.coroutines.future.await
34+
import kotlinx.coroutines.runBlocking
35+
import org.junit.jupiter.api.Test
36+
import java.util.concurrent.ExecutorService
37+
import java.util.concurrent.Executors
38+
39+
class SuspendingHandlerTest {
40+
41+
companion object {
42+
val executorService: ExecutorService = Executors.newSingleThreadExecutor()
43+
}
44+
45+
@Test
46+
fun test(): Unit = runBlocking {
47+
val manager = TestCommandManager()
48+
49+
manager.buildAndRegister("suspend") {
50+
suspendingHandler {
51+
println("called from thread: ${Thread.currentThread().name}")
52+
53+
someSuspendingFunction()
54+
}
55+
}
56+
57+
manager.executeCommand(TestCommandSender(), "suspend").await()
58+
}
59+
60+
suspend fun someSuspendingFunction() {}
61+
62+
private class TestCommandSender
63+
64+
private class TestCommandManager : CommandManager<TestCommandSender>(
65+
AsynchronousCommandExecutionCoordinator.newBuilder<TestCommandSender>()
66+
.withExecutor(executorService)
67+
.build(),
68+
CommandRegistrationHandler.nullCommandRegistrationHandler()
69+
) {
70+
71+
override fun hasPermission(sender: TestCommandSender, permission: String): Boolean = true
72+
73+
override fun createDefaultCommandMeta(): CommandMeta = SimpleCommandMeta.empty()
74+
}
75+
}

gradle/libs.versions.yml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ plugins:
1111
versions:
1212
kotlin: &kotlin 1.5.31
1313
dokka: *kotlin
14+
coroutines: 1.5.2
1415
checkerQual: 3.14.0
1516

1617
# build-logic
@@ -26,6 +27,15 @@ dependencies:
2627
name: checker-qual
2728
version: { ref: checkerQual }
2829

30+
coroutinesCore:
31+
group: org.jetbrains.kotlinx
32+
name: kotlinx-coroutines-core
33+
version: { ref: coroutines }
34+
coroutinesJdk8:
35+
group: org.jetbrains.kotlinx
36+
name: kotlinx-coroutines-jdk8
37+
version: { ref: coroutines }
38+
2939
# build-logic
3040
indraCommon:
3141
group: net.kyori
@@ -61,3 +71,6 @@ dependencies:
6171
version: { ref: ktlint }
6272

6373
bundles:
74+
coroutines:
75+
- coroutinesCore
76+
- coroutinesJdk8

settings.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ include(":cloud-annotations")
2424

2525
// Kotlin Extensions
2626
setupKotlinModule("cloud-kotlin-extensions")
27+
setupKotlinModule("cloud-kotlin-coroutines")
2728
setupKotlinModule("cloud-kotlin-coroutines-annotations")
2829

2930
// Discord Modules

0 commit comments

Comments
 (0)