-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathWispProcessor.kt
More file actions
126 lines (104 loc) Β· 4.66 KB
/
WispProcessor.kt
File metadata and controls
126 lines (104 loc) Β· 4.66 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
package com.angrypodo.wisp
import com.angrypodo.wisp.WispValidator.validateDuplicatePaths
import com.angrypodo.wisp.annotations.Wisp
import com.angrypodo.wisp.generator.RouteFactoryGenerator
import com.angrypodo.wisp.generator.WispRegistryGenerator
import com.angrypodo.wisp.mapper.toRouteInfo
import com.angrypodo.wisp.model.RouteInfo
import com.google.devtools.ksp.processing.CodeGenerator
import com.google.devtools.ksp.processing.Dependencies
import com.google.devtools.ksp.processing.KSPLogger
import com.google.devtools.ksp.processing.Resolver
import com.google.devtools.ksp.processing.SymbolProcessor
import com.google.devtools.ksp.symbol.KSAnnotated
import com.google.devtools.ksp.symbol.KSClassDeclaration
import com.google.devtools.ksp.symbol.KSFile
import com.google.devtools.ksp.validate
import com.squareup.kotlinpoet.ksp.writeTo
/**
* @Wisp μ΄λ
Έν
μ΄μ
μ΄ λΆμ ν΄λμ€λ₯Ό μ°Ύμ μ ν¨μ±μ κ²μ¦νκ³ μ½λλ₯Ό μμ±νλ λ©μΈ νλ‘μΈμ ν΄λμ€μ
λλ€.
*/
internal class WispProcessor(
private val codeGenerator: CodeGenerator,
private val logger: KSPLogger
) : SymbolProcessor {
private val factoryGenerator = RouteFactoryGenerator(logger)
private val registryGenerator = WispRegistryGenerator()
override fun process(resolver: Resolver): List<KSAnnotated> {
val symbols = resolver.getSymbolsWithAnnotation(WISP_ANNOTATION)
.filterIsInstance<KSClassDeclaration>()
if (symbols.none()) return emptyList()
val (processableSymbols, deferredSymbols) = symbols.partition { it.validate() }
val routesWithSymbols = processableSymbols.mapNotNull { routeClass ->
val routeInfo = processSymbol(routeClass) ?: return@mapNotNull null
routeInfo to routeClass
}
val routeInfos = routesWithSymbols.map { it.first }
val duplicateValidationResult = validateDuplicatePaths(routeInfos)
if (duplicateValidationResult is WispValidator.ValidationResult.Failure) {
duplicateValidationResult.errors.forEach { logger.error(it) }
return deferredSymbols
}
if (routesWithSymbols.isNotEmpty()) {
val sourceFiles = routesWithSymbols.mapNotNull { it.second.containingFile }.distinct()
generateRouteRegistry(routeInfos, sourceFiles)
}
return deferredSymbols
}
private fun processSymbol(routeClass: KSClassDeclaration): RouteInfo? {
if (!validateSerializable(routeClass)) return null
val routeInfo = routeClass.toRouteInfo() ?: run {
logInvalidRouteError(routeClass)
return null
}
generateRouteFactory(routeClass, routeInfo)
return routeInfo
}
private fun validateSerializable(routeClass: KSClassDeclaration): Boolean {
if (routeClass.hasSerializableAnnotation()) return true
val routeName = routeClass.qualifiedName?.asString()
logger.error(
"Wisp Error: Route '$routeName' must be annotated with @Serializable.",
routeClass
)
return false
}
private fun logInvalidRouteError(routeClass: KSClassDeclaration) {
val routeName = routeClass.simpleName.asString()
logger.error(
"Wisp Error: Route '$routeName' is missing @Wisp path or has invalid parameters.",
routeClass
)
}
private fun generateRouteFactory(
routeClass: KSClassDeclaration,
routeInfo: RouteInfo
) {
val fileSpec = factoryGenerator.generate(routeInfo)
val dependencies = Dependencies(false, routeClass.containingFile!!)
fileSpec.writeTo(codeGenerator, dependencies)
}
private fun generateRouteRegistry(
routeInfos: List<RouteInfo>,
sourceFiles: List<KSFile>
) {
val fileSpec = registryGenerator.generate(routeInfos)
val dependencies = Dependencies(true, *sourceFiles.toTypedArray())
fileSpec.writeTo(codeGenerator, dependencies)
}
private fun KSClassDeclaration.hasSerializableAnnotation(): Boolean {
return annotations.any { annotation ->
val shortName = annotation.shortName.asString()
val qualifiedName = annotation.annotationType.resolve()
.declaration.qualifiedName?.asString()
val isSerializable = shortName == SERIALIZABLE_SHORT_NAME &&
qualifiedName == SERIALIZABLE_ANNOTATION
isSerializable
}
}
companion object {
private val WISP_ANNOTATION = requireNotNull(Wisp::class.qualifiedName)
private const val SERIALIZABLE_SHORT_NAME = "Serializable"
private const val SERIALIZABLE_ANNOTATION = "kotlinx.serialization.Serializable"
}
}