A KSP to shift annotation processing to runtime.
Annotation Processing in general is not appealing for use in application code since, first, enumerating annotated elements is near impossible while, second, writing an annotation processor is not a simple and straightforward task.
The missing piece to achieve effective runtime annotation processing is the ability to enumerate annotated elements.
This feature is the purpose of this KSP.
First, the user defines a custom annotation
annotated by @Enumerable
Second, the user annotates elements with the custom annotation.
Then, the KSP writes the signatures of the annotated elements to the resources.
Meanwhile, the user uses elementsWith()
to
enumerate the annotated elements.
Finally, at runtime, the code of elementsWith()
reads the signatures in the resources, obtain
runtime reflection instances, ensure elements are
actually annotated with the target annotation and
return the result.
To add this KSP and its runtime to your project:
// settings.gradle.kts
pluginManagement {
repositories {
gradlePluginPortal()
}
}
// build.gradle.kts
plugins {
// ...
id("com.google.devtools.ksp") version "1.8.0-1.0.9"
}
repositories {
// ...
mavenCentral()
maven { url = uri("https://jitpack.io") }
}
dependencies {
// Replace TAG with the desired version
ksp("org.cufy.ranno:ranno-ksp:TAG")
implementation("org.cufy.ranno:ranno-runtime:TAG")
}
// To process @Enumerable annotations from different modules
// builtin annotations does not need to be added here
ksp {
arg("ranno.external", listOf(
"org.cufy.ranno.Enumerated",
"org.cufy.ranno.ktor.EnumeratedRoute",
"org.cufy.ranno.ktor.EnumeratedApplication",
).joinToString(" "))
}
Define your custom annotation
@Enumerable
annotation class MyAnnotation(
val value: String = ""
)
Annotate your elements
@MyAnnotation
fun hello() {
println("Hello World")
}
@MyAnnotation
fun bye() {
println("Bye World")
}
Enumerate your elements
fun main() {
// to get a list of all kinds of annotated elements
elementsWith<MyAnnotation>().forEach {
println(it)
}
// a filter block can be passed
elementsWith<MyAnnotation> { it.value == "" }
// or a shortcut can be used
functionsWith<MyAnnotation>()
classesWith<MyAnnotation>()
propertiesWith<MyAnnotation>()
// to run the annotated elements safely
val r0 = runWith<MyAnnotation>(arg0, arg1, arg2 /* ... */)
// ^ the result of invoking all the elements in an unordered list
// a filter block can be passed
val r1 = runWith<MyAnnotation>(arg0, arg1, arg2 /* ... */) {
it.value == ""
}
// another way of running the annotated elements
val r2 = arg0.applyWith<MyAnnotation>(arg1, arg2 /* ... */)
}