@@ -2,33 +2,37 @@ package kategory.optics
2
2
3
3
import com.google.auto.service.AutoService
4
4
import kategory.common.utils.AbstractProcessor
5
+ import kategory.common.utils.asClassOrPackageDataWrapper
6
+ import kategory.common.utils.isSealed
5
7
import kategory.common.utils.knownError
6
8
import me.eugeniomarletti.kotlin.metadata.KotlinClassMetadata
9
+ import me.eugeniomarletti.kotlin.metadata.extractFullName
7
10
import me.eugeniomarletti.kotlin.metadata.isDataClass
8
11
import me.eugeniomarletti.kotlin.metadata.kotlinMetadata
9
- import me.eugeniomarletti.kotlin.metadata.modality
12
+ import me.eugeniomarletti.kotlin.metadata.proto
10
13
import org.jetbrains.kotlin.serialization.ProtoBuf
11
14
import java.io.File
12
15
import javax.annotation.processing.Processor
13
16
import javax.annotation.processing.RoundEnvironment
14
17
import javax.lang.model.SourceVersion
15
- import javax.lang.model.element.Element
16
- import javax.lang.model.element.ElementKind
17
- import javax.lang.model.element.ExecutableElement
18
18
import javax.lang.model.element.TypeElement
19
19
20
+ import javax.lang.model.element.Element
21
+
20
22
@AutoService(Processor ::class )
21
23
class OptikalProcessor : AbstractProcessor () {
22
24
23
- private val annotatedLenses = mutableListOf<AnnotatedLens . Element >()
25
+ private val annotatedLenses = mutableListOf<AnnotatedOptic >()
24
26
25
- private val annotatedPrisms = mutableListOf<AnnotatedPrism . Element >()
27
+ private val annotatedPrisms = mutableListOf<AnnotatedOptic >()
26
28
27
- override fun getSupportedSourceVersion (): SourceVersion = SourceVersion .latestSupported ()
29
+ private val annotatedIsos = mutableListOf< AnnotatedOptic > ()
28
30
31
+ override fun getSupportedSourceVersion (): SourceVersion = SourceVersion .latestSupported()
29
32
override fun getSupportedAnnotationTypes () = setOf (
30
33
lensesAnnotationClass.canonicalName,
31
- prismsAnnotationClass.canonicalName
34
+ prismsAnnotationClass.canonicalName,
35
+ isosAnnotationClass.canonicalName
32
36
)
33
37
34
38
override fun onProcess (annotations : Set <TypeElement >, roundEnv : RoundEnvironment ) {
@@ -40,35 +44,40 @@ class OptikalProcessor : AbstractProcessor() {
40
44
.getElementsAnnotatedWith(prismsAnnotationClass)
41
45
.map(this ::evalAnnotatedPrismElement)
42
46
47
+ annotatedIsos + = roundEnv
48
+ .getElementsAnnotatedWith(isosAnnotationClass)
49
+ .map(this ::evalAnnotatedIsoElement)
50
+
43
51
if (roundEnv.processingOver()) {
44
52
val generatedDir = File (this .generatedDir!! , " " ).also { it.mkdirs() }
45
53
LensesFileGenerator (annotatedLenses, generatedDir).generate()
46
54
PrismsFileGenerator (annotatedPrisms, generatedDir).generate()
55
+ IsosFileGenerator (annotatedIsos, generatedDir).generate()
47
56
}
48
57
}
49
58
50
- private fun evalAnnotatedElement (element : Element ): AnnotatedLens . Element = when {
59
+ private fun evalAnnotatedElement (element : Element ): AnnotatedOptic = when {
51
60
element.let { it.kotlinMetadata as ? KotlinClassMetadata }?.data?.classProto?.isDataClass == true ->
52
- AnnotatedLens . Element (
61
+ AnnotatedOptic (
53
62
element as TypeElement ,
54
- element.enclosedElements.firstOrNull { it.kind == ElementKind .CONSTRUCTOR }
55
- ?.let { it as ExecutableElement }
56
- ?.parameters ? : emptyList()
63
+ getClassData(element),
64
+ getConstructorTypesNames(element).zip(getConstructorParamNames(element), ::Target )
57
65
)
58
66
59
67
else -> knownError(opticsAnnotationError(element, lensesAnnotationName, lensesAnnotationTarget))
60
68
}
61
69
62
- private fun evalAnnotatedPrismElement (element : Element ): AnnotatedPrism . Element = when {
70
+ private fun evalAnnotatedPrismElement (element : Element ): AnnotatedOptic = when {
63
71
element.let { it.kotlinMetadata as ? KotlinClassMetadata }?.data?.classProto?.isSealed == true -> {
64
72
val (nameResolver, classProto) = element.kotlinMetadata.let { it as KotlinClassMetadata }.data
65
73
66
- AnnotatedPrism . Element (
74
+ AnnotatedOptic (
67
75
element as TypeElement ,
76
+ getClassData(element),
68
77
classProto.sealedSubclassFqNameList
69
78
.map(nameResolver::getString)
70
79
.map { it.replace(' /' , ' .' ) }
71
- .mapNotNull(elementUtils::getTypeElement)
80
+ .map { Target (it, it.substringAfterLast( " . " )) }
72
81
)
73
82
}
74
83
@@ -79,7 +88,41 @@ class OptikalProcessor : AbstractProcessor() {
79
88
|Cannot use $annotationName on ${element.enclosingElement} .${element.simpleName} .
80
89
|It can only be used on $targetName .""" .trimMargin()
81
90
82
- private val ProtoBuf .Class .isSealed
83
- get() = modality == ProtoBuf .Modality .SEALED
91
+ private fun evalAnnotatedIsoElement (element : Element ): AnnotatedOptic = when {
92
+ (element.kotlinMetadata as ? KotlinClassMetadata )?.data?.classProto?.isDataClass == true -> {
93
+ val properties = getConstructorTypesNames(element).zip(getConstructorParamNames(element), ::Target )
94
+
95
+ if (properties.size < 2 || properties.size > 10 )
96
+ knownError(" ${element.enclosingElement} .${element.simpleName} constructor parameters should be between 2 and 10" )
97
+ else
98
+ AnnotatedOptic (element as TypeElement , getClassData(element), properties)
99
+ }
100
+
101
+ else -> knownError(opticsAnnotationError(element, isosAnnotationName, isosAnnotationTarget))
102
+ }
103
+
104
+ private fun getConstructorTypesNames (element : Element ): List <String > = element.kotlinMetadata
105
+ .let { it as KotlinClassMetadata }.data
106
+ .let { data ->
107
+ data.proto.constructorOrBuilderList
108
+ .first()
109
+ .valueParameterList
110
+ .map { it.type.extractFullName(data) }
111
+ }
112
+
113
+ private fun getConstructorParamNames (element : Element ): List <String > = element.kotlinMetadata
114
+ .let { it as KotlinClassMetadata }.data
115
+ .let { (nameResolver, classProto) ->
116
+ classProto.constructorOrBuilderList
117
+ .first()
118
+ .valueParameterList
119
+ .map(ProtoBuf .ValueParameter ::getName)
120
+ .map(nameResolver::getString)
121
+ }
122
+
123
+ private fun getClassData (element : Element ) = element.kotlinMetadata
124
+ .let { it as KotlinClassMetadata }
125
+ .data
126
+ .asClassOrPackageDataWrapper(elementUtils.getPackageOf(element).toString())
84
127
85
128
}
0 commit comments