1
1
/*
2
- * Copyright (c) 2024. ForteScarlet.
2
+ * Copyright (c) 2024-2025 . ForteScarlet.
3
3
*
4
4
* This file is part of simbot-component-qq-guild.
5
5
*
@@ -28,6 +28,7 @@ import com.google.devtools.ksp.symbol.KSPropertyDeclaration
28
28
import com.google.devtools.ksp.symbol.Modifier
29
29
import com.squareup.kotlinpoet.*
30
30
import com.squareup.kotlinpoet.ParameterizedTypeName.Companion.parameterizedBy
31
+ import com.squareup.kotlinpoet.jvm.jvmInline
31
32
import com.squareup.kotlinpoet.jvm.jvmName
32
33
import com.squareup.kotlinpoet.jvm.jvmStatic
33
34
import com.squareup.kotlinpoet.ksp.toClassName
@@ -44,6 +45,10 @@ private const val EVENT_INTENTS_PACKAGE = "love.forte.simbot.qguild.event"
44
45
private const val EVENT_INTENTS_CLASS_NAME = " EventIntents"
45
46
private val EventIntentsClassName = ClassName (EVENT_INTENTS_PACKAGE , EVENT_INTENTS_CLASS_NAME )
46
47
48
+ private const val INTENTS_APPENDER_PACKAGE = " love.forte.simbot.qguild.event"
49
+ private const val INTENTS_APPENDER_CLASS_NAME = " IntentsAppender"
50
+ private val IntentsAppenderClassName = ClassName (INTENTS_APPENDER_PACKAGE , INTENTS_APPENDER_CLASS_NAME )
51
+
47
52
private const val INTENTS_CONST_NAME = " INTENTS"
48
53
49
54
private const val INTENTS_PACKAGE = " love.forte.simbot.qguild.event"
@@ -64,6 +69,15 @@ private const val EVENT_NAME_BASED_MARKER_PKG = "love.forte.simbot.qguild.intern
64
69
internal class EventIntentsAggregationProcessor (
65
70
val environment : SymbolProcessorEnvironment
66
71
) : SymbolProcessor {
72
+ data class NamesToType (
73
+ val names : Set <String >,
74
+ val declaration : KSClassDeclaration ,
75
+ val firstUpper : String ,
76
+ val firstLower : String ,
77
+ val snackUpper : String ,
78
+ val snackLower : String ,
79
+ )
80
+
67
81
private val processed = AtomicBoolean (false )
68
82
69
83
override fun process (resolver : Resolver ): List <KSAnnotated > {
@@ -78,32 +92,69 @@ internal class EventIntentsAggregationProcessor(
78
92
.getSealedSubclasses()
79
93
.toList()
80
94
81
- // val allSubObjects = resolver.getAllFiles()
82
- // .filterIsInstance<KSClassDeclaration>()
83
- // .flatMap { dec ->
84
- // sequence {
85
- // yield(dec)
86
- // yieldAll(dec.getSealedSubclasses())
87
- // }
88
- // }
89
- // .filter { declaration ->
90
- // declaration.classKind == ClassKind.OBJECT &&
91
- // declaration.asStarProjectedType()
92
- // .isAssignableFrom(eventIntentsDeclaration.asStarProjectedType())
93
- // }
94
- // .toList()
95
-
96
95
environment.logger.info(" Found sub object: $allSubObjects " )
97
96
98
97
val aggregationBuilder = TypeSpec .objectBuilder(AGGREGATION_OBJ_NAME )
99
98
100
99
generateAll(aggregationBuilder, allSubObjects)
101
- generateGetByName(aggregationBuilder, allSubObjects)
100
+
101
+ // name -> type
102
+ val nameToTypes = allSubObjects.map { declaration ->
103
+ val nameBasedAnnotation = declaration.annotations
104
+ .firstOrNull {
105
+ (it.annotationType.resolve().declaration as ? KSClassDeclaration )?.let { annoDecl ->
106
+ annoDecl.simpleName.asString() == EVENT_NAME_BASED_MARKER_NAME
107
+ && annoDecl.packageName.asString() == EVENT_NAME_BASED_MARKER_PKG
108
+ } == true
109
+ }
110
+
111
+ fun baseName (): String = declaration.simpleName.asString()
112
+
113
+ fun findFromAnnotation (name : String ): String? =
114
+ nameBasedAnnotation?.arguments?.firstOrNull {
115
+ it.name?.asString() == name
116
+ }?.value as ? String?
117
+
118
+ val firstUpper = findFromAnnotation(" firstUpper" )
119
+ ?.takeUnless { it.isBlank() }
120
+ ? : baseName()
121
+ val firstLower = findFromAnnotation(" firstLower" )
122
+ ?.takeUnless { it.isBlank() }
123
+ ? : baseName().replaceFirstChar(Char ::lowercaseChar)
124
+ val snackUpper = findFromAnnotation(" snackUpper" )
125
+ ?.takeUnless { it.isBlank() }
126
+ ? : baseName().toSnack(true )
127
+ val snackLower = findFromAnnotation(" snackLower" )
128
+ ?.takeUnless { it.isBlank() }
129
+ ? : baseName().toSnack(false )
130
+
131
+
132
+ val set = setOf (
133
+ firstUpper,
134
+ firstLower,
135
+ snackUpper,
136
+ snackLower,
137
+ )
138
+
139
+ NamesToType (
140
+ set,
141
+ declaration,
142
+ firstUpper,
143
+ firstLower,
144
+ snackUpper,
145
+ snackLower
146
+ )
147
+ }
148
+
149
+ generateGetByName(aggregationBuilder, nameToTypes)
150
+
151
+ val intentsAppenderOpTypeSpec = generateIntentsAppenderOp(nameToTypes)
102
152
103
153
val fileBuilder = FileSpec .builder(OUTPUT_PACKAGE , AGGREGATION_FILE_NAME )
104
154
fileBuilder.addType(aggregationBuilder.build())
155
+ fileBuilder.addType(intentsAppenderOpTypeSpec)
105
156
fileBuilder.addFileComment(
106
- " 本文件内容为自动生成 ,生成于 %L" ,
157
+ " \n 本文件内容为自动生成 ,生成于 %L\n " ,
107
158
OffsetDateTime .now(ZoneOffset .ofHours(8 )).toString()
108
159
)
109
160
@@ -196,53 +247,12 @@ internal class EventIntentsAggregationProcessor(
196
247
builder.addFunction(allIntentsFunc.build())
197
248
}
198
249
250
+
199
251
/* *
200
252
* 生成根据名称获取结果的 `getByName(name: String)`,
201
253
* 名称支持驼峰、全大写和全小写。
202
254
*/
203
- private fun generateGetByName (builder : TypeSpec .Builder , list : List <KSClassDeclaration >) {
204
- data class NamesToType (val names : Set <String >, val declaration : KSClassDeclaration )
205
-
206
- val nameToTypes = list.map { declaration ->
207
- val nameBasedAnnotation = declaration.annotations
208
- .firstOrNull {
209
- (it.annotationType.resolve().declaration as ? KSClassDeclaration )?.let { annoDecl ->
210
- annoDecl.simpleName.asString() == EVENT_NAME_BASED_MARKER_NAME
211
- && annoDecl.packageName.asString() == EVENT_NAME_BASED_MARKER_PKG
212
- } ? : false
213
- }
214
-
215
- fun baseName (): String = declaration.simpleName.asString()
216
-
217
- fun findFromAnnotation (name : String ): String? =
218
- nameBasedAnnotation?.arguments?.firstOrNull {
219
- it.name?.asString() == name
220
- }?.value as ? String?
221
-
222
- val firstUpper = findFromAnnotation(" firstUpper" )
223
- ?.takeUnless { it.isBlank() }
224
- ? : baseName()
225
- val firstLower = findFromAnnotation(" firstLower" )
226
- ?.takeUnless { it.isBlank() }
227
- ? : baseName().replaceFirstChar(Char ::lowercaseChar)
228
- val snackUpper = findFromAnnotation(" snackUpper" )
229
- ?.takeUnless { it.isBlank() }
230
- ? : baseName().toSnack(true )
231
- val snackLower = findFromAnnotation(" snackLower" )
232
- ?.takeUnless { it.isBlank() }
233
- ? : baseName().toSnack(false )
234
-
235
-
236
- val set = setOf (
237
- firstUpper,
238
- firstLower,
239
- snackUpper,
240
- snackLower,
241
- )
242
-
243
- NamesToType (set, declaration)
244
- }
245
-
255
+ private fun generateGetByName (builder : TypeSpec .Builder , nameToTypes : List <NamesToType >) {
246
256
val doc = CodeBlock .builder().apply {
247
257
addStatement(" 使用简单的字符串名称来获取一个对应的 [%T] 子类型的 intents 值," , EventIntentsClassName )
248
258
addStatement(" 字符串名称与这个类型的简单类型相关:类名的名称,以及对应的snack(下滑线)格式。" )
@@ -295,6 +305,45 @@ internal class EventIntentsAggregationProcessor(
295
305
}
296
306
297
307
308
+ /* *
309
+ * 生成 `IntentsAppenderOp`
310
+ *
311
+ * ```kotlin
312
+ * @JvmInline
313
+ * value class IntentsAppenderOp (private val appender: IntentsAppender) {
314
+ * fun guilds() { appender.appendIntents(EventIntents.Guilds.intents) }
315
+ * fun groupAndC2C() { ... }
316
+ * // ...
317
+ * }
318
+ * ```
319
+ */
320
+ private fun generateIntentsAppenderOp (nameToTypes : List <NamesToType >): TypeSpec {
321
+ val builder = TypeSpec .classBuilder(" IntentsAppenderOp" )
322
+ .addModifiers(KModifier .PUBLIC , KModifier .VALUE )
323
+ .jvmInline()
324
+ .primaryConstructor(
325
+ FunSpec .constructorBuilder().apply {
326
+ addParameter(" appender" , IntentsAppenderClassName )
327
+ }.build()
328
+ )
329
+ .addProperty(
330
+ PropertySpec .builder(" appender" , IntentsAppenderClassName , KModifier .PRIVATE )
331
+ .initializer(" appender" )
332
+ .build()
333
+ )
334
+
335
+ nameToTypes.forEach { nameToType ->
336
+ builder.addFunction(FunSpec .builder(nameToType.firstLower).apply {
337
+ addModifiers(KModifier .PUBLIC )
338
+ addCode(
339
+ " appender.appendIntents(%T.intents)" ,
340
+ nameToType.declaration.asStarProjectedType().toClassName()
341
+ )
342
+ }.build())
343
+ }
344
+
345
+ return builder.build()
346
+ }
298
347
}
299
348
300
349
private fun String.toSnack (allUpper : Boolean ): String = buildString(length) {
0 commit comments