|
| 1 | +package me.snoty.integration.plugin.processor |
| 2 | + |
| 3 | +import com.google.devtools.ksp.KspExperimental |
| 4 | +import com.google.devtools.ksp.getAnnotationsByType |
| 5 | +import com.google.devtools.ksp.processing.* |
| 6 | +import com.google.devtools.ksp.symbol.KSAnnotated |
| 7 | +import com.google.devtools.ksp.symbol.KSClassDeclaration |
| 8 | +import com.squareup.kotlinpoet.* |
| 9 | +import com.squareup.kotlinpoet.ksp.toClassName |
| 10 | +import com.squareup.kotlinpoet.ksp.writeTo |
| 11 | +import com.squareup.kotlinpoet.metadata.specs.toTypeSpec |
| 12 | +import me.snoty.integration.common.annotation.RegisterNode |
| 13 | +import me.snoty.integration.common.wiring.node.NodeDescriptor |
| 14 | +import me.snoty.integration.common.wiring.node.NodeHandlerContributor |
| 15 | + |
| 16 | +class NodeHandlerContributorProcessor(val logger: KSPLogger, private val codeGenerator: CodeGenerator) : SymbolProcessor { |
| 17 | + override fun process(resolver: Resolver): List<KSAnnotated> { |
| 18 | + resolver.getSymbolsWithAnnotation(RegisterNode::class.qualifiedName!!) |
| 19 | + .filterIsInstance<KSClassDeclaration>() |
| 20 | + .forEach { processClass(it) } |
| 21 | + |
| 22 | + return emptyList() |
| 23 | + } |
| 24 | + |
| 25 | + @OptIn(KspExperimental::class) |
| 26 | + private fun processClass(clazz: KSClassDeclaration) { |
| 27 | + val node = clazz.getAnnotationsByType(RegisterNode::class).first() |
| 28 | + |
| 29 | + val contributorClassName = ClassName(clazz.packageName.asString(), "${clazz.simpleName.asString()}Contributor") |
| 30 | + val classBuilder = TypeSpec.classBuilder(contributorClassName) |
| 31 | + val fileSpec = FileSpec.builder(contributorClassName) |
| 32 | + |
| 33 | + classBuilder |
| 34 | + .addSuperinterface(NodeHandlerContributor::class) |
| 35 | + .addFunction(createNodeHandlerContributorFun(clazz, node)) |
| 36 | + |
| 37 | + // write SPI file |
| 38 | + codeGenerator.createNewFileByPath( |
| 39 | + dependencies = Dependencies(aggregating = false, clazz.containingFile!!), |
| 40 | + path = "META-INF/services/${NodeHandlerContributor::class.qualifiedName}", |
| 41 | + extensionName = "" |
| 42 | + ).writer().use { |
| 43 | + it.appendLine(contributorClassName.canonicalName) |
| 44 | + } |
| 45 | + |
| 46 | + // write contributor file |
| 47 | + fileSpec |
| 48 | + .addType(classBuilder.build()) |
| 49 | + .build() |
| 50 | + .writeTo( |
| 51 | + codeGenerator = codeGenerator, |
| 52 | + aggregating = false, |
| 53 | + originatingKSFiles = listOf(clazz.containingFile!!) |
| 54 | + ) |
| 55 | + } |
| 56 | + |
| 57 | + private fun createNodeHandlerContributorFun(handler: KSClassDeclaration, node: RegisterNode): FunSpec { |
| 58 | + val abstractFun = NodeHandlerContributor::class.toTypeSpec(lenient = true) |
| 59 | + .funSpecs |
| 60 | + .first() |
| 61 | + val funSpec = abstractFun |
| 62 | + .toBuilder() |
| 63 | + .apply { |
| 64 | + modifiers -= KModifier.ABSTRACT |
| 65 | + modifiers += KModifier.OVERRIDE |
| 66 | + } |
| 67 | + .addStatement("val descriptor = %T(%L, %L)", NodeDescriptor::class.asTypeName(), "\"${node.subsystem}\"", "\"${node.type}\"") |
| 68 | + .addStatement("val nodeContext = nodeContextBuilder(descriptor)") |
| 69 | + .addStatement("val handler = %T(nodeContext)", handler.toClassName()) |
| 70 | + .addStatement("registry.registerHandler(descriptor, handler)") |
| 71 | + |
| 72 | + return funSpec.build() |
| 73 | + } |
| 74 | + |
| 75 | + class Provider : SymbolProcessorProvider { |
| 76 | + override fun create(environment: SymbolProcessorEnvironment): SymbolProcessor { |
| 77 | + return NodeHandlerContributorProcessor(environment.logger, environment.codeGenerator) |
| 78 | + } |
| 79 | + } |
| 80 | +} |
0 commit comments