Add ksp to generate MangaSource enum
This commit is contained in:
7
kotatsu-parsers-ksp/build.gradle
Normal file
7
kotatsu-parsers-ksp/build.gradle
Normal file
@@ -0,0 +1,7 @@
|
||||
plugins {
|
||||
id 'org.jetbrains.kotlin.jvm'
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation 'com.google.devtools.ksp:symbol-processing-api:1.6.20-1.0.5'
|
||||
}
|
||||
@@ -0,0 +1,142 @@
|
||||
package org.koitharu.kotatsu.parsers.ksp
|
||||
|
||||
import com.google.devtools.ksp.isAbstract
|
||||
import com.google.devtools.ksp.processing.*
|
||||
import com.google.devtools.ksp.symbol.ClassKind
|
||||
import com.google.devtools.ksp.symbol.KSAnnotated
|
||||
import com.google.devtools.ksp.symbol.KSClassDeclaration
|
||||
import com.google.devtools.ksp.symbol.KSVisitorVoid
|
||||
import com.google.devtools.ksp.validate
|
||||
import java.io.Writer
|
||||
|
||||
class ParserProcessor(
|
||||
private val codeGenerator: CodeGenerator,
|
||||
private val logger: KSPLogger,
|
||||
private val options: Map<String, String>,
|
||||
) : SymbolProcessor {
|
||||
|
||||
override fun process(resolver: Resolver): List<KSAnnotated> {
|
||||
val symbols = resolver
|
||||
.getSymbolsWithAnnotation("org.koitharu.kotatsu.parsers.MangaSourceParser")
|
||||
val ret = symbols.filterNot { it.validate() }.toList()
|
||||
if (!symbols.iterator().hasNext()) {
|
||||
return ret
|
||||
}
|
||||
val dependencies = Dependencies.ALL_FILES
|
||||
val factoryFile = try {
|
||||
codeGenerator.createNewFile(
|
||||
dependencies = dependencies,
|
||||
packageName = "org.koitharu.kotatsu.parsers",
|
||||
fileName = "MangaParserFactory",
|
||||
)
|
||||
} catch (e: FileAlreadyExistsException) {
|
||||
logger.warn(e.toString(), null)
|
||||
null
|
||||
}
|
||||
val sourcesFile = try {
|
||||
codeGenerator.createNewFile(
|
||||
dependencies = dependencies,
|
||||
packageName = "org.koitharu.kotatsu.parsers.model",
|
||||
fileName = "MangaSource",
|
||||
)
|
||||
} catch (e: FileAlreadyExistsException) {
|
||||
logger.warn(e.toString(), null)
|
||||
null
|
||||
}
|
||||
sourcesFile?.writer().use { sourcesWriter ->
|
||||
factoryFile?.writer().use { factoryWriter ->
|
||||
writeContent(sourcesWriter, factoryWriter, symbols)
|
||||
}
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
private fun writeContent(
|
||||
sourcesWriter: Writer?,
|
||||
factoryWriter: Writer?,
|
||||
symbols: Sequence<KSAnnotated>,
|
||||
) {
|
||||
if (sourcesWriter == null && factoryWriter == null) {
|
||||
return
|
||||
}
|
||||
factoryWriter?.write(
|
||||
"""
|
||||
package org.koitharu.kotatsu.parsers
|
||||
|
||||
import org.koitharu.kotatsu.parsers.model.MangaSource
|
||||
|
||||
fun MangaSource.newParser(context: MangaLoaderContext): MangaParser = when (this) {
|
||||
|
||||
""".trimIndent(),
|
||||
)
|
||||
sourcesWriter?.write(
|
||||
"""
|
||||
package org.koitharu.kotatsu.parsers.model
|
||||
|
||||
import org.koitharu.kotatsu.parsers.model.MangaSource
|
||||
|
||||
enum class MangaSource(
|
||||
val title: String,
|
||||
val locale: String?,
|
||||
) {
|
||||
LOCAL("Local", null),
|
||||
|
||||
""".trimIndent(),
|
||||
)
|
||||
|
||||
val visitor = ParserVisitor(sourcesWriter, factoryWriter)
|
||||
symbols
|
||||
.filter { it is KSClassDeclaration && it.validate() }
|
||||
.forEach { it.accept(visitor, Unit) }
|
||||
|
||||
factoryWriter?.write(
|
||||
"""
|
||||
MangaSource.LOCAL -> throw NotImplementedError("Local manga parser is not supported")
|
||||
}.also {
|
||||
require(it.source == this) {
|
||||
"Cannot instantiate manga parser: ${'$'}name mapped to ${'$'}{it.source}"
|
||||
}
|
||||
}
|
||||
""".trimIndent(),
|
||||
)
|
||||
sourcesWriter?.write(
|
||||
"""
|
||||
;
|
||||
}
|
||||
""".trimIndent(),
|
||||
)
|
||||
}
|
||||
|
||||
private inner class ParserVisitor(
|
||||
private val sourcesWriter: Writer?,
|
||||
private val factoryWriter: Writer?,
|
||||
) : KSVisitorVoid() {
|
||||
|
||||
override fun visitClassDeclaration(classDeclaration: KSClassDeclaration, data: Unit) {
|
||||
if (classDeclaration.classKind != ClassKind.CLASS || classDeclaration.isAbstract()) {
|
||||
logger.error("Only non-abstract can be annotated with @MangaSourceParser", classDeclaration)
|
||||
}
|
||||
val annotation = classDeclaration.annotations.single { it.shortName.asString() == "MangaSourceParser" }
|
||||
val name = annotation.arguments.single { it.name?.asString() == "name" }.value as String
|
||||
val title = annotation.arguments.single { it.name?.asString() == "title" }.value as String
|
||||
val locale = annotation.arguments.single { it.name?.asString() == "locale" }.value as String
|
||||
val localeString = if (locale.isEmpty()) "null" else "\"$locale\""
|
||||
|
||||
if (name.any { it.isLowerCase() }) {
|
||||
logger.error("Manga source name must be uppercase: $name")
|
||||
}
|
||||
|
||||
val constructor = classDeclaration.primaryConstructor
|
||||
if (constructor == null || constructor.parameters.count { !it.hasDefault } != 1) {
|
||||
logger.error(
|
||||
"Class with @MangaSourceParser must have a primary constructor with one parameter",
|
||||
classDeclaration,
|
||||
)
|
||||
}
|
||||
|
||||
val className = classDeclaration.qualifiedName?.asString()
|
||||
factoryWriter?.write("\tMangaSource.$name -> $className(context)\n")
|
||||
sourcesWriter?.write("\t$name(\"$title\", $localeString),\n")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package org.koitharu.kotatsu.parsers.ksp
|
||||
|
||||
import com.google.devtools.ksp.processing.SymbolProcessor
|
||||
import com.google.devtools.ksp.processing.SymbolProcessorEnvironment
|
||||
import com.google.devtools.ksp.processing.SymbolProcessorProvider
|
||||
|
||||
class ParserProcessorProvider : SymbolProcessorProvider {
|
||||
|
||||
override fun create(environment: SymbolProcessorEnvironment): SymbolProcessor {
|
||||
return ParserProcessor(
|
||||
codeGenerator = environment.codeGenerator,
|
||||
logger = environment.logger,
|
||||
options = environment.options
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
org.koitharu.kotatsu.parsers.ksp.ParserProcessorProvider
|
||||
Reference in New Issue
Block a user