Skip to content

Commit 57ea374

Browse files
committed
Generate SerialName mapping functions for serializable enums
1 parent f39450a commit 57ea374

File tree

2 files changed

+83
-0
lines changed

2 files changed

+83
-0
lines changed
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
package com.github.ephemient.kotlinx.serialization.contrib.processor
2+
3+
import com.google.auto.service.AutoService
4+
import com.google.devtools.ksp.KspExperimental
5+
import com.google.devtools.ksp.getAnnotationsByType
6+
import com.google.devtools.ksp.processing.Dependencies
7+
import com.google.devtools.ksp.processing.Resolver
8+
import com.google.devtools.ksp.processing.SymbolProcessor
9+
import com.google.devtools.ksp.processing.SymbolProcessorEnvironment
10+
import com.google.devtools.ksp.processing.SymbolProcessorProvider
11+
import com.google.devtools.ksp.symbol.ClassKind
12+
import com.google.devtools.ksp.symbol.KSAnnotated
13+
import com.google.devtools.ksp.symbol.KSClassDeclaration
14+
import com.squareup.kotlinpoet.FileSpec
15+
import com.squareup.kotlinpoet.FunSpec
16+
import com.squareup.kotlinpoet.STRING
17+
import com.squareup.kotlinpoet.ksp.KotlinPoetKspPreview
18+
import com.squareup.kotlinpoet.ksp.toClassName
19+
import com.squareup.kotlinpoet.ksp.writeTo
20+
import kotlinx.serialization.SerialName
21+
import kotlinx.serialization.Serializable
22+
23+
class EnumSerialNamesProcessor(environment: SymbolProcessorEnvironment) : SymbolProcessor {
24+
private val codeGenerator = environment.codeGenerator
25+
26+
@OptIn(KotlinPoetKspPreview::class, KspExperimental::class)
27+
override fun process(resolver: Resolver): List<KSAnnotated> {
28+
for (enum in resolver.getSymbolsWithAnnotation(Serializable::class.qualifiedName!!, false)) {
29+
if (enum !is KSClassDeclaration || enum.classKind != ClassKind.ENUM_CLASS) continue
30+
val enumClass = enum.asStarProjectedType().toClassName()
31+
32+
val entries = enum.declarations
33+
.filterIsInstance<KSClassDeclaration>()
34+
.filter { it.classKind == ClassKind.ENUM_ENTRY }
35+
.associate { entry ->
36+
val name = entry.simpleName.asString()
37+
val serialName = entry.getAnnotationsByType(SerialName::class)
38+
.firstNotNullOfOrNull { it.value.ifEmpty { null } }
39+
name to (serialName ?: name)
40+
}
41+
42+
FileSpec.builder(enumClass.packageName, enumClass.simpleNames.joinToString("_", postfix = "SerialNames"))
43+
.addFunction(
44+
FunSpec.builder("toSerialName")
45+
.receiver(enumClass)
46+
.returns(STRING)
47+
.beginControlFlow("return when (this)")
48+
.apply {
49+
for ((name, serialName) in entries) {
50+
addStatement("%T.%N -> %S", enumClass, name, serialName)
51+
}
52+
}
53+
.endControlFlow()
54+
.build()
55+
)
56+
.addFunction(
57+
FunSpec.builder(enumClass.simpleNames.joinToString("", "fromSerialNameTo", "OrNull"))
58+
.receiver(STRING)
59+
.returns(enumClass.copy(nullable = true))
60+
.beginControlFlow("return when (this)")
61+
.apply {
62+
for ((name, serialName) in entries) {
63+
addStatement("%S -> %T.%N", serialName, enumClass, name)
64+
}
65+
}
66+
.addStatement("else -> null")
67+
.endControlFlow()
68+
.build()
69+
)
70+
.build()
71+
.writeTo(codeGenerator, Dependencies(false, *listOfNotNull(enum.containingFile).toTypedArray()))
72+
}
73+
return emptyList()
74+
}
75+
76+
@AutoService(SymbolProcessorProvider::class)
77+
class Provider : SymbolProcessorProvider {
78+
override fun create(environment: SymbolProcessorEnvironment): SymbolProcessor =
79+
EnumSerialNamesProcessor(environment)
80+
}
81+
}

test/src/commonMain/kotlin/Example.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,4 +19,6 @@ enum class Example {
1919
@SerialName("FORCE")
2020
Force,
2121
;
22+
23+
override fun toString(): String = toSerialName()
2224
}

0 commit comments

Comments
 (0)