Coverage Summary for Class: WrappedKeysEmitter (com.ghost.serialization.compiler)
| Class |
Class, %
|
Method, %
|
Branch, %
|
Line, %
|
Instruction, %
|
| WrappedKeysEmitter |
100%
(1/1)
|
88.9%
(8/9)
|
68.2%
(15/22)
|
92.4%
(85/92)
|
85.1%
(478/562)
|
package com.ghost.serialization.compiler
import com.squareup.kotlinpoet.ClassName
import com.squareup.kotlinpoet.CodeBlock
import com.squareup.kotlinpoet.KModifier
import com.squareup.kotlinpoet.ParameterizedTypeName.Companion.parameterizedBy
import com.squareup.kotlinpoet.PropertySpec
import com.squareup.kotlinpoet.TypeSpec
import com.squareup.kotlinpoet.asClassName
import com.ghost.serialization.compiler.GhostEmitterConstants as C
/**
* Code generation helpers for [@GhostWrappedKeys][com.ghost.serialization.annotations.GhostWrappedKeys].
*/
internal object WrappedKeysEmitter {
fun captureVariableName(prop: GhostPropertyModel): String =
C.STR_WRAPPED_CAPTURE_PREFIX + prop.kotlinName
fun hasWrappedProperties(properties: List<GhostPropertyModel>): Boolean =
properties.any { it.wrappedSourceKeys != null }
fun wrappedKeyDispatch(properties: List<GhostPropertyModel>): Map<String, Pair<GhostPropertyModel, Int>> {
val map = linkedMapOf<String, Pair<GhostPropertyModel, Int>>()
properties.forEach { prop ->
val keys = prop.wrappedSourceKeys ?: return@forEach
keys.forEachIndexed { index, key ->
map[key] = prop to index
}
}
return map
}
fun emitCaptureVariables(body: CodeBlock.Builder, properties: List<GhostPropertyModel>) {
properties.forEach { prop ->
val keys = prop.wrappedSourceKeys ?: return@forEach
body.addStatement(
C.TEMPLATE_WRAPPED_CAPTURE_VAR,
captureVariableName(prop),
ClassName(C.PKG_PARSER, C.STR_GHOST_WRAPPED_KEYS_CAPTURE),
keys.size,
)
}
}
fun emitWrappedKeyCapture(
body: CodeBlock.Builder,
prop: GhostPropertyModel,
keyIndex: Int,
) {
body.addStatement(
C.TEMPLATE_CAPTURE_WRAPPED_KEY,
captureVariableName(prop),
keyIndex,
)
}
fun emitPostLoopDeserialization(
body: CodeBlock.Builder,
properties: List<GhostPropertyModel>,
propertyIndices: Map<GhostPropertyModel, Int>,
readerClass: ClassName,
) {
properties.forEach { prop ->
val keys = prop.wrappedSourceKeys ?: return@forEach
val index = propertyIndices[prop] ?: return@forEach
val captureName = captureVariableName(prop)
val maskIdx = index / C.MASK_SIZE_BITS.toInt()
val constName = C.STR_MASK_PREFIX + prop.kotlinName.uppercase()
val varName = C.TEMPLATE_VAR_NAME.format(prop.kotlinName)
val keyLiteralsName = wrappedKeyLiteralsConstantName(prop)
val omitAbsentName = wrappedOmitAbsentConstantName(prop)
body.addStatement(
C.TEMPLATE_WRAPPED_JSON_MATERIALIZE,
prop.kotlinName,
captureName,
keyLiteralsName,
prop.wrappedOmitIfEmpty,
omitAbsentName,
)
body.beginControlFlow(C.TEMPLATE_WRAPPED_JSON_IF_NOT_NULL, prop.kotlinName)
body.addStatement(
C.TEMPLATE_WRAPPED_READER_VAR,
readerClass,
prop.kotlinName,
)
val call = CodeBlock.of(
C.TEMPLATE_DESERIALIZE_WRAPPED_READER,
prop.type.serializerClassName(),
)
if (prop.isResilient) {
body.beginControlFlow(C.TEMPLATE_DECODE_RESILIENT, call)
body.addStatement(varName + C.TEMPLATE_ASSIGN_L, C.STR_IT)
body.addStatement(C.STR_MASK_BITWISE_OR, maskIdx, maskIdx, constName)
body.endControlFlow()
} else {
body.addStatement(varName + C.TEMPLATE_ASSIGN_L, call)
body.addStatement(C.STR_MASK_BITWISE_OR, maskIdx, maskIdx, constName)
}
body.nextControlFlow(C.STR_ELSE)
if (prop.isNullable) {
body.addStatement(C.TEMPLATE_NULL_ASSIGN, varName)
}
body.endControlFlow()
}
}
fun addWrappedKeyConstants(
typeSpecBuilder: TypeSpec.Builder,
properties: List<GhostPropertyModel>,
) {
properties.forEach { prop ->
val keys = prop.wrappedSourceKeys ?: return@forEach
val literalsName = wrappedKeyLiteralsConstantName(prop)
if (typeSpecBuilder.propertySpecs.any { it.name == literalsName }) {
return@forEach
}
val initializer = CodeBlock.builder()
.add(C.TEMPLATE_ARRAY_OF_OPEN)
keys.forEachIndexed { index, key ->
if (index > 0) {
initializer.add(C.STR_COMMA_NEWLINE)
}
val quotedKey = C.STR_JSON_KEY_QUOTE + key + C.STR_JSON_KEY_COLON_SUFFIX
initializer.add(C.TEMPLATE_WRAPPED_KEY_LITERAL_BYTE, quotedKey)
}
initializer.add(C.STR_NEWLINE_CLOSE_PAREN)
typeSpecBuilder.addProperty(
PropertySpec.builder(
literalsName,
Array::class.asClassName().parameterizedBy(ByteArray::class.asClassName()),
)
.addModifiers(KModifier.PRIVATE)
.initializer(initializer.build())
.build(),
)
val omitAbsentName = wrappedOmitAbsentConstantName(prop)
val absentIndices = prop.wrappedOmitIfAbsent.map { absentKey ->
keys.indexOf(absentKey)
}.filter { it >= 0 }
typeSpecBuilder.addProperty(
PropertySpec.builder(omitAbsentName, IntArray::class)
.addModifiers(KModifier.PRIVATE)
.initializer(
C.TEMPLATE_INT_ARRAY_OF,
absentIndices.joinToString(C.STR_COMMA),
)
.build(),
)
}
}
private fun wrappedKeyLiteralsConstantName(prop: GhostPropertyModel): String =
C.STR_WRAPPED_KEY_LITERALS_PREFIX + prop.kotlinName.uppercase()
private fun wrappedOmitAbsentConstantName(prop: GhostPropertyModel): String =
C.STR_WRAPPED_OMIT_ABSENT_PREFIX + prop.kotlinName.uppercase()
}