Skip to content

Commit

Permalink
Merge pull request #161 from simple-robot/dev/support-quote-msg-element
Browse files Browse the repository at this point in the history
支持KookQuote的获取和MessageReference的解析
  • Loading branch information
ForteScarlet authored Aug 6, 2024
2 parents ca56600 + 887ea1d commit c25292a
Show file tree
Hide file tree
Showing 16 changed files with 549 additions and 141 deletions.
50 changes: 50 additions & 0 deletions internal-processors/message-element-processor/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/*
* Copyright (c) 2024. ForteScarlet.
*
* This file is part of simbot-component-kook.
*
* simbot-component-kook is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* simbot-component-kook is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with simbot-component-kook,
* If not, see <https://www.gnu.org/licenses/>.
*/

import org.jetbrains.kotlin.gradle.dsl.JvmTarget

plugins {
kotlin("jvm")
}

repositories {
mavenCentral()
}

kotlin {
jvmToolchain(11)
compilerOptions {
javaParameters = true
jvmTarget.set(JvmTarget.JVM_11)
}
}

configJavaCompileWithModule()

dependencies {
api(libs.ksp)
api(libs.kotlinPoet.ksp)
testImplementation(kotlin("test-junit5"))
}

tasks.getByName<Test>("test") {
useJUnitPlatform()
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
/*
* Copyright (c) 2024. ForteScarlet.
*
* This file is part of simbot-component-kook.
*
* simbot-component-kook is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* simbot-component-kook is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with simbot-component-kook,
* If not, see <https://www.gnu.org/licenses/>.
*/

package kook.internal.processors.msgelement

import com.google.devtools.ksp.getClassDeclarationByName
import com.google.devtools.ksp.isAbstract
import com.google.devtools.ksp.processing.Resolver
import com.google.devtools.ksp.processing.SymbolProcessor
import com.google.devtools.ksp.processing.SymbolProcessorEnvironment
import com.google.devtools.ksp.symbol.KSAnnotated
import com.google.devtools.ksp.symbol.KSClassDeclaration
import com.google.devtools.ksp.symbol.Modifier
import com.squareup.kotlinpoet.*
import com.squareup.kotlinpoet.ParameterizedTypeName.Companion.parameterizedBy
import com.squareup.kotlinpoet.ksp.toClassName
import com.squareup.kotlinpoet.ksp.writeTo
import java.time.Instant
import java.time.ZoneOffset


private const val BASE_ELEMENT_CLASS_PACKAGE = "love.forte.simbot.component.kook.message"
private const val BASE_ELEMENT_CLASS_SIMPLE_NAME = "KookMessageElement"

private const val BASE_ELEMENT_CLASS_NAME =
"$BASE_ELEMENT_CLASS_PACKAGE.$BASE_ELEMENT_CLASS_SIMPLE_NAME"

private val BaseElementClassName =
ClassName(BASE_ELEMENT_CLASS_PACKAGE, BASE_ELEMENT_CLASS_SIMPLE_NAME)

private const val KTX_SERIALIZABLE_ANNOTATION_PACKAGE = "kotlinx.serialization"
private const val KTX_SERIALIZABLE_ANNOTATION_SIMPLE_NAME = "Serializable"
private const val KTX_SERIALIZABLE_ANNOTATION_NAME =
"$KTX_SERIALIZABLE_ANNOTATION_PACKAGE.$KTX_SERIALIZABLE_ANNOTATION_SIMPLE_NAME"

private val PolymorphicModuleBuilderClassName =
ClassName("kotlinx.serialization.modules", "PolymorphicModuleBuilder")

private const val OUTPUT_PACKAGE = "love.forte.simbot.component.kook.message"
private const val OUTPUT_FILE = "IncludeKookMessageElements.generated"
private const val OUTPUT_FUN_NAME = "includeKookMessageElements"

/**
*
* @author ForteScarlet
*/
class MessageElementProcessor(environment: SymbolProcessorEnvironment) : SymbolProcessor {
private val codeGenerator = environment.codeGenerator
private val elementDeclarations = mutableListOf<KSClassDeclaration>()

override fun finish() {
val newFun = resolve(elementDeclarations)

val fileSpec = FileSpec.Companion.builder(
OUTPUT_PACKAGE, OUTPUT_FILE
)
.addFunction(newFun)
.addFileComment("\nAuto-Generated at ${Instant.now().atOffset(ZoneOffset.ofHours(8))}\n")
.build()

fileSpec.writeTo(
codeGenerator = codeGenerator,
aggregating = true,
originatingKSFiles = buildList {
for (impl in elementDeclarations) {
impl.containingFile?.also { add(it) }
}
}
)
}

override fun process(resolver: Resolver): List<KSAnnotated> {
val baseClass = resolver.getClassDeclarationByName(
BASE_ELEMENT_CLASS_NAME
) ?: error("Base class $BASE_ELEMENT_CLASS_NAME not found")

val baseClassStarType = baseClass.asStarProjectedType()

resolver
.getSymbolsWithAnnotation(KTX_SERIALIZABLE_ANNOTATION_NAME)
.filterIsInstance<KSClassDeclaration>()
.filter { declaration ->
// 是 base class 的子类
baseClassStarType.isAssignableFrom(declaration.asStarProjectedType())
}
.filter { declaration ->
// 不是抽象的、不是sealed的可序列化类
!declaration.isAbstract() && !declaration.modifiers.contains(Modifier.SEALED)
}
.toCollection(elementDeclarations)

return emptyList()
}

/**
* 生成函数:
*
* ```kotlin
* internal fun PolymorphicModuleBuilder<KookMessageElement>.includeKookMessageElements() {
* subclass(KookAsset::class, KookAsset.serializer())
* // ...
* }
* ```
*/
private fun resolve(declarations: List<KSClassDeclaration>): FunSpec {
val receiverType = PolymorphicModuleBuilderClassName.parameterizedBy(BaseElementClassName)
// val optAnnotation =
val optAnnotation = AnnotationSpec.builder(ClassName("kotlin", "OptIn"))
.addMember(
"%T::class, %T::class",
ClassName("love.forte.simbot.annotations", "ExperimentalSimbotAPI"),
ClassName("love.forte.simbot.kook", "ExperimentalKookApi")
)
.build()

val internalAPIAnnotation = ClassName("love.forte.simbot.kook", "InternalKookApi")

return FunSpec.builder(OUTPUT_FUN_NAME).apply {
modifiers.add(KModifier.INTERNAL)
receiver(receiverType)
addAnnotation(optAnnotation)
addAnnotation(internalAPIAnnotation)

for (declaration in declarations) {
val className = declaration.toClassName()
addCode("subclass(%T::class, %T.serializer())\n", className, className)
}
}.build()
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/*
* Copyright (c) 2024. ForteScarlet.
*
* This file is part of simbot-component-kook.
*
* simbot-component-kook is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* simbot-component-kook is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with simbot-component-kook,
* If not, see <https://www.gnu.org/licenses/>.
*/

package kook.internal.processors.msgelement

import com.google.devtools.ksp.processing.SymbolProcessor
import com.google.devtools.ksp.processing.SymbolProcessorEnvironment
import com.google.devtools.ksp.processing.SymbolProcessorProvider

/**
*
* @author ForteScarlet
*/
class MessageElementProcessorProvider : SymbolProcessorProvider {
override fun create(environment: SymbolProcessorEnvironment): SymbolProcessor =
MessageElementProcessor(environment)
}

Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
kook.internal.processors.msgelement.MessageElementProcessorProvider
24 changes: 14 additions & 10 deletions settings.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,24 +1,28 @@
/*
* Copyright (c) 2022-2023. ForteScarlet.
* Copyright (c) 2022-2024. ForteScarlet.
*
* This file is part of simbot-component-kook.
* This file is part of simbot-component-kook.
*
* simbot-component-kook is free software: you can redistribute it and/or modify it under the terms of
* the GNU Lesser General Public License as published by the Free Software Foundation,
* either version 3 of the License, or (at your option) any later version.
* simbot-component-kook is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* simbot-component-kook is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU Lesser General Public License for more details.
* simbot-component-kook is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along with simbot-component-kook,
* If not, see <https://www.gnu.org/licenses/>.
* You should have received a copy of the GNU Lesser General Public License
* along with simbot-component-kook,
* If not, see <https://www.gnu.org/licenses/>.
*/

rootProject.name = "simbot-component-kook"

// internals
include(":internal-processors:api-reader")
include(":internal-processors:message-element-processor")

include("simbot-component-kook-api")
include("simbot-component-kook-stdlib")
Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,21 @@
/*
* Copyright (c) 2023. ForteScarlet.
* Copyright (c) 2023-2024. ForteScarlet.
*
* This file is part of simbot-component-kook.
* This file is part of simbot-component-kook.
*
* simbot-component-kook is free software: you can redistribute it and/or modify it under the terms of
* the GNU Lesser General Public License as published by the Free Software Foundation,
* either version 3 of the License, or (at your option) any later version.
* simbot-component-kook is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* simbot-component-kook is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU Lesser General Public License for more details.
* simbot-component-kook is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along with simbot-component-kook,
* If not, see <https://www.gnu.org/licenses/>.
* You should have received a copy of the GNU Lesser General Public License
* along with simbot-component-kook,
* If not, see <https://www.gnu.org/licenses/>.
*/

package love.forte.simbot.kook
Expand Down Expand Up @@ -82,6 +85,7 @@ public object Kook {
useArrayPolymorphism = false
// see https://github.com/kaiheila/api-docs/issues/174
explicitNulls = false
coerceInputValues = true
}
}

Expand Down
Loading

0 comments on commit c25292a

Please sign in to comment.