Skip to content

Commit

Permalink
Added new event listener generation system (#165)
Browse files Browse the repository at this point in the history
Updated Gradle version + dependencies versions
  • Loading branch information
RealYusufIsmail committed Jun 17, 2023
1 parent afb9b7e commit 798239a
Show file tree
Hide file tree
Showing 125 changed files with 652 additions and 1,070 deletions.
15 changes: 11 additions & 4 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ buildscript {
dependencies {
classpath("org.jetbrains.dokka:dokka-base:1.8.10")
classpath("io.codearte.gradle.nexus:gradle-nexus-staging-plugin:0.30.0")
classpath("com.squareup:kotlinpoet:" + properties["kotlinPoetVersion"])
}
}

Expand Down Expand Up @@ -53,10 +54,9 @@ apply(from = "gradle/tasks/eventClassJavaDocChecker.gradle")

apply(from = "gradle/tasks/Nexus.gradle")

repositories {
maven("https://s01.oss.sonatype.org/content/repositories/snapshots/")
mavenCentral()
}
apply(from = "gradle/tasks/generateEvents.gradle.kts")

repositories { mavenCentral() }

dependencies {
// json
Expand Down Expand Up @@ -99,6 +99,11 @@ tasks.test {

tasks.withType<KotlinCompile> { kotlinOptions.jvmTarget = "11" }

tasks {
val compileKotlinTask = named<org.jetbrains.kotlin.gradle.tasks.KotlinCompile>("compileKotlin")
compileKotlinTask.configure { dependsOn("generateEvents") }
}

tasks.build {
// dependsOn on custom tasks
dependsOn(tasks.getByName("checkEvents")) // check if events are valid
Expand Down Expand Up @@ -348,3 +353,5 @@ tasks.getByName("dokkaHtml", DokkaTask::class) {
}
}
}

sourceSets { main { kotlin { srcDirs("src/main/kotlin", "build/generated/kotlin") } } }
21 changes: 11 additions & 10 deletions gradle.properties
Original file line number Diff line number Diff line change
@@ -1,27 +1,28 @@
org.gradle.daemon=true
kotlin.code.style=official
version = 1.6.5-SNAPSHOT
version = 1.6.5

## Dependencies Versions and Plugin

# Plugins
jvmVersion = 1.8.10
pluginAllOpenVersion = 1.8.10
spotlessVersion = 6.12.0
dokkaVersion = 1.8.10
detektVersion = 1.22.0
benManesVersion = 0.46.0
spotlessVersion = 6.19.0
dokkaVersion = 1.8.20
detektVersion = 1.23.0
benManesVersion = 0.47.0

# Dependencies
jacksonModuleKotlinVersion = 2.14.2
logBackClassicVersion = 1.4.5
logBackCoreVersion = 1.4.5
jacksonModuleKotlinVersion = 2.15.2
logBackClassicVersion = 1.4.8
logBackCoreVersion = 1.4.8
sysoutOverSlf4jVersion = 1.0.2
jconfigVersion = 1.0.8
okhttp3Version = 5.0.0-alpha.11
nvWebsocketClientVersion = 2.14
ydeVersion = 1.0.8.1-SNAPSHOT
ydeVersion = 1.0.8
kotlinTestVersion = 1.8.10
kotlinxCoroutinesCoreVersion = 1.6.4
kotlinxCoroutinesCoreVersion = 1.7.1
jsr305Version = 3.0.2
xsalsa20poly1305Version = 0.11.0
kotlinPoetVersion = 1.14.2
293 changes: 293 additions & 0 deletions gradle/tasks/generateEvents.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,293 @@
import com.squareup.kotlinpoet.*
import java.io.File

buildscript {
repositories { mavenCentral() }

dependencies { classpath("com.squareup:kotlinpoet:" + properties["kotlinPoetVersion"]) }
}

fun addFunction(isFirstFunc: Boolean): FunSpec.Builder {
val onFunction =
FunSpec.builder("on")
.addKdoc(
"""
Listens for events of type [T].
@param consumer The consumer function to handle the event.
@return The [T] event listener.
"""
.trimIndent())
.addModifiers(KModifier.INLINE)
.addTypeVariable(TypeVariableName("reified T : GenericEvent"))
.addParameter(
"consumer",
LambdaTypeName.get(
receiver = ClassName("io.github.ydwk.ydwk.evm.event", "GenericEvent"),
parameters =
listOf(ParameterSpec.builder("", TypeVariableName("T")).build()),
returnType = UNIT)
.copy(suspending = true),
KModifier.CROSSINLINE)
.returns(ClassName("io.github.ydwk.ydwk.evm.event", "CoroutineEventListener"))
.addCode(
"""
return object : CoroutineEventListener {
override suspend fun onEvent(event: GenericEvent) {
if (event is T) {
event.consumer(event)
}
}
}
"""
.trimIndent())

if (isFirstFunc) {
// add @Deprecated("Switch to new event listener",
// ReplaceWith("ydwk.eventListener.on<T>(consumer)"), DeprecationLevel.WARNING)
onFunction.addAnnotation(
AnnotationSpec.builder(Deprecated::class)
.addMember("\"Switch to event listener\"")
.addMember("ReplaceWith(\"ydwk.eventListener.on<T>(consumer)\")")
.addMember("DeprecationLevel.WARNING")
.build())

onFunction.addCode(".also { this.addEventListeners(it) }")
} else {
onFunction.addCode(".also { ydwk.addEventListeners(it) }")
}

return onFunction
}

fun addEventSpecification(name: String): FunSpec {
return FunSpec.builder("on${name}")
.addKdoc(
"""
Listens for [${name}].
@param consumer The consumer function to handle the ${name}.
@return The [${name}] listener.
"""
.trimIndent())
.addParameter(
"consumer",
LambdaTypeName.get(
receiver = ClassName("io.github.ydwk.ydwk.evm.event", "GenericEvent"),
parameters = listOf(ParameterSpec.builder("", TypeVariableName(name)).build()),
returnType = UNIT)
.copy(suspending = true),
KModifier.CROSSINLINE)
.addModifiers(KModifier.PUBLIC, KModifier.INLINE)
.returns(ClassName("io.github.ydwk.ydwk.evm.event", "CoroutineEventListener"))
.addCode(
"""
return on<${name}>(consumer)
"""
.trimIndent())
.build()
}

fun generateEventFile() {
val eventsFolder = File(projectDir, "src/main/kotlin/io/github/ydwk/ydwk/evm/event/events")

val channelEvents = mutableMapOf<String, String>()
val gatewayEvents = mutableMapOf<String, String>()
val guildEvents = mutableMapOf<String, String>()
val guildModerationEvents = mutableMapOf<String, String>()
val interactionEvents = mutableMapOf<String, String>()
val userEvents = mutableMapOf<String, String>()
val voiceEvents = mutableMapOf<String, String>()

eventsFolder.walk().forEach {
if (it.isFile && it.name.endsWith(".kt")) {
val file = it.readText()
val name = it.name.substring(0, it.name.length - 3)
when {
file.contains("@ChannelEvent") -> {
channelEvents[name] = it.absolutePath
}
file.contains("@GatewayEvent") -> {
gatewayEvents[name] = it.absolutePath
}
file.contains("@GuildEvent") -> {
guildEvents[name] = it.absolutePath
}
file.contains("@GuildModerationEvent") -> {
guildModerationEvents[name] = it.absolutePath
}
file.contains("@InteractionEvent") -> {
interactionEvents[name] = it.absolutePath
}
file.contains("@UserEvent") -> {
userEvents[name] = it.absolutePath
}
file.contains("@VoiceEvent") -> {
voiceEvents[name] = it.absolutePath
}
}
}
}

val eventClass =
TypeSpec.classBuilder("EventListeners")
.primaryConstructor(
FunSpec.constructorBuilder()
.addParameter("ydwk", ClassName("io.github.ydwk.ydwk", "YDWK"))
.build())
.addProperties(
listOf(
PropertySpec.builder("ydwk", ClassName("io.github.ydwk.ydwk", "YDWK"))
.initializer("ydwk")
.build()))
.addModifiers(KModifier.PUBLIC)
.addFunction(addFunction(false).build())

for ((name, _) in channelEvents) {
// do public inline fun on{name}Event { return on<name>Event> }

eventClass.addFunction(addEventSpecification(name))
}

for ((name, _) in gatewayEvents) {
eventClass.addFunction(addEventSpecification(name))
}

for ((name, _) in guildEvents) {
eventClass.addFunction(addEventSpecification(name))
}

for ((name, _) in guildModerationEvents) {
eventClass.addFunction(addEventSpecification(name))
}

for ((name, _) in interactionEvents) {
eventClass.addFunction(addEventSpecification(name))
}

for ((name, _) in userEvents) {
eventClass.addFunction(addEventSpecification(name))
}

for ((name, _) in voiceEvents) {
eventClass.addFunction(addEventSpecification(name))
}

val eventFile =
FileSpec.builder("io.github.ydwk.ydwk.evm.event", "EventListeners")
.addImport("io.github.ydwk.ydwk", "YDWK")
.addImport("io.github.ydwk.ydwk.evm.backend.event", "GenericEvent")
.addImport("io.github.ydwk.ydwk.evm.backend.event", "CoroutineEventListener")
.addType(eventClass.build())
.addFunction(
addFunction(true)
.addModifiers(KModifier.PUBLIC)
.receiver(ClassName("io.github.ydwk.ydwk.evm.event", "YDWK"))
.build())

for ((name, path) in channelEvents) {
eventFile.addImport(
path.substringAfter("src/main/kotlin/").substringBeforeLast("/").replace("/", "."),
name)
}

for ((name, path) in gatewayEvents) {
eventFile.addImport(
path.substringAfter("src/main/kotlin/").substringBeforeLast("/").replace("/", "."),
name)
}

for ((name, path) in guildEvents) {
eventFile.addImport(
path.substringAfter("src/main/kotlin/").substringBeforeLast("/").replace("/", "."),
name)
}

for ((name, path) in guildModerationEvents) {
eventFile.addImport(
path.substringAfter("src/main/kotlin/").substringBeforeLast("/").replace("/", "."),
name)
}

for ((name, path) in interactionEvents) {
eventFile.addImport(
path.substringAfter("src/main/kotlin/").substringBeforeLast("/").replace("/", "."),
name)
}

for ((name, path) in userEvents) {
eventFile.addImport(
path.substringAfter("src/main/kotlin/").substringBeforeLast("/").replace("/", "."),
name)
}

for ((name, path) in voiceEvents) {
eventFile.addImport(
path.substringAfter("src/main/kotlin/").substringBeforeLast("/").replace("/", "."),
name)
}

File("${project.buildDir}/generated/kotlin").apply {
mkdirs()

eventFile.build().writeTo(this)
}

generateEventListenersInterfaces("ChannelEventListener", channelEvents)
generateEventListenersInterfaces("GatewayEventListener", gatewayEvents)
generateEventListenersInterfaces("GuildEventListener", guildEvents)
generateEventListenersInterfaces("GuildModerationEventListener", guildModerationEvents)
generateEventListenersInterfaces("InteractionEventListener", interactionEvents)
generateEventListenersInterfaces("UserEventListener", userEvents)
generateEventListenersInterfaces("VoiceEventListener", voiceEvents)
}

fun generateEventListenersInterfaces(listenerName: String, listenerEvents: Map<String, String>) {
val interfaceClass =
TypeSpec.interfaceBuilder(listenerName)
.addModifiers(KModifier.PUBLIC)
.addSuperinterface(ClassName("io.github.ydwk.ydwk.evm.backend.event", "IEventListener"))

val onEventFunction =
FunSpec.builder("onEvent")
.addModifiers(KModifier.PUBLIC, KModifier.OVERRIDE)
.addParameter(
"event", ClassName("io.github.ydwk.ydwk.evm.backend.event", "GenericEvent"))
.beginControlFlow("when (event)")

for ((name, path) in listenerEvents) {
val eventClassName =
ClassName(
path.substringAfter("src/main/kotlin/").substringBeforeLast("/").replace("/", "."),
name)
interfaceClass.addFunction(
FunSpec.builder("on$name")
.addKdoc("Listens to the $name.\n\n@param event The $name.")
.addModifiers(KModifier.PUBLIC)
.addParameter("event", eventClassName)
.build())

onEventFunction.addStatement("is %T -> on$name(event)", eventClassName)
}

onEventFunction.endControlFlow()

interfaceClass.addFunction(onEventFunction.build())

val interfaceFile =
FileSpec.builder("io.github.ydwk.ydwk.evm.listeners", listenerName)
.addImport("io.github.ydwk.ydwk.evm.backend.event", "IEventListener")
.addType(interfaceClass.build())
.build()

File("${project.buildDir}/generated/kotlin").apply {
mkdirs()
interfaceFile.writeTo(this)
}
}

tasks.create("generateEvents") {
group = "generate"

doLast { generateEventFile() }
}
2 changes: 1 addition & 1 deletion gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.0.2-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.1.1-bin.zip
networkTimeout=10000
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
Loading

0 comments on commit 798239a

Please sign in to comment.