Coverage Summary for Class: GhostPropertyExtensionsKt (com.ghost.serialization.compiler)

Class Class, % Method, % Branch, % Line, % Instruction, %
GhostPropertyExtensionsKt 100% (1/1) 50% (3/6) 45.7% (42/92) 35.1% (34/97) 26.5% (205/773)


 package com.ghost.serialization.compiler
 
 import com.squareup.kotlinpoet.TypeName
 import com.ghost.serialization.compiler.GhostEmitterConstants as C
 
 /**
  * Extension methods for [GhostPropertyModel] to facilitate code generation.
  *
  * These helpers encapsulate the logic for determining variable types, initial values,
  * and return expressions based on the property's metadata (e.g., nullability,
  * primitive types, value classes).
  */
 
 /**
  * Resolves the appropriate KotlinPoet [TypeName] to declare the local tracking variable
  * for this property. Handled cases include:
  * - Non-nullable primitives: mapped directly to their unboxed Kotlin/JVM types.
  * - Non-nullable inline/value classes: mapped to the underlying unboxed primitive type.
  * - Nullable/Object types: coerced to a nullable KotlinPoet type representation.
  */
 internal fun GhostPropertyModel.getVariableType(): TypeName {
     val isPrimitive = type.isPrimitive() && !isNullable
     val isUnboxedValueClass = isValueClass && valueClassProperty != null && !isNullable
 
     return when {
         isPrimitive -> typeName
         isUnboxedValueClass -> {
             val underlying = valueClassProperty!!
             if (underlying.type.isPrimitive()) {
                 underlying.typeName
             } else {
                 underlying.typeName.copy(nullable = true)
             }
         }
 
         else -> typeName.copy(nullable = true)
     }
 }
 
 /**
  * Resolves the initial placeholder value literal representation for the tracking variable.
  * For example: `null` for objects, `0` for Int, `0L` for Long, `false` for Boolean.
  */
 internal fun GhostPropertyModel.getInitialValue(): String {
     val isUnboxedValueClass = isValueClass && valueClassProperty != null && !isNullable
     val targetProp = if (isUnboxedValueClass) valueClassProperty!! else this
 
     return when {
         isNullable -> C.STR_NULL
         targetProp.type.isPrimitiveInt() -> C.STR_ZERO
         targetProp.type.isPrimitiveLong() -> C.STR_ZERO_L
         targetProp.type.isPrimitiveDouble() -> C.STR_ZERO_D
         targetProp.type.isPrimitiveFloat() -> C.STR_ZERO_F
         targetProp.type.isPrimitiveByte() -> C.STR_ZERO
         targetProp.type.isPrimitiveShort() -> C.STR_ZERO
         targetProp.type.isPrimitiveChar() -> C.STR_CHAR_NULL_LITERAL
         targetProp.type.isPrimitiveBoolean() -> C.STR_FALSE
         else -> C.STR_NULL
     }
 }
 
 /**
  * Generates the expression string used to pass this property to the constructor
  * in the standard deserializer return statement. Appends a null-assertion operator `!!`
  * if the parameter is non-nullable but tracked as a nullable local variable.
  */
 internal fun GhostPropertyModel.getReturnExpression(): String {
     val isPrimitive = type.isPrimitive() && !isNullable
     val isUnboxedValueClass = isValueClass && valueClassProperty != null && !isNullable
     val varName = C.TEMPLATE_VAR_NAME.format(kotlinName)
 
     return when {
         isPrimitive -> varName
         isUnboxedValueClass -> {
             val bang = if (valueClassProperty!!.type.isPrimitive()) {
                 C.STR_EMPTY
             } else {
                 C.STR_BANG_BANG
             }
             C.TEMPLATE_WRAP_TYPE.format(typeName, "$varName$bang")
         }
 
         else -> {
             if (isNullable) {
                 varName
             } else {
                 "$varName${C.STR_BANG_BANG}"
             }
         }
     }
 }
 
 /**
  * Generates the conditional fallback return expression when standard deserialization
  * supports default arguments. If the property mask bit is set, it resolves to the parsed variable,
  * otherwise it resolves to the copy-based result field value.
  *
  * @param maskIdx Index of the tracking bitmask variable (e.g. `_mask0`).
  * @param bitMaskStr String representation of the bitmask representing this property.
  */
 internal fun GhostPropertyModel.getDefaultValueReturnExpression(
     maskIdx: Int,
     bitMaskStr: String
 ): String {
     val isPrimitive = type.isPrimitive() && !isNullable
     val isUnboxedValueClass = isValueClass && valueClassProperty != null && !isNullable
 
     val maskName = C.TEMPLATE_MASK_VAR.format(maskIdx)
     val varName = C.TEMPLATE_VAR_NAME.format(kotlinName)
     val resultVar = C.TEMPLATE_RESULT_VAR.format(kotlinName)
 
     return when {
         isNullable || isPrimitive ->
             C.TEMPLATE_IF_MASK_RETURN.format(maskName, bitMaskStr, varName, resultVar)
 
         isUnboxedValueClass -> {
             val bang = if (valueClassProperty!!.type.isPrimitive()) {
                 C.STR_EMPTY
             } else {
                 C.STR_BANG_BANG
             }
             C.TEMPLATE_IF_MASK_RETURN.format(
                 maskName,
                 bitMaskStr,
                 C.TEMPLATE_WRAP_TYPE.format(typeName, "$varName$bang"),
                 resultVar
             )
         }
 
         else -> {
             val value = "$varName${C.STR_BANG_BANG}"
             C.TEMPLATE_IF_MASK_RETURN.format(
                 maskName,
                 bitMaskStr,
                 value,
                 resultVar
             )
         }
     }
 }
 
 /**
  * Generates the return expression string pointing to the generated `DecodingContext`
  * during fragmented deserialization. Handles boxing/unboxing for value classes and nullability.
  */
 internal fun GhostPropertyModel.getFragmentedReturnExpression(): String {
     val isPrimitive = type.isPrimitive() && !isNullable
     val isUnboxedValueClass = isValueClass && valueClassProperty != null && !isNullable
     val ctxVar = C.TEMPLATE_CTX_VAR.format(kotlinName)
 
     return when {
         isPrimitive -> ctxVar
         isUnboxedValueClass -> {
             val bang = if (valueClassProperty!!.type.isPrimitive()) {
                 C.STR_EMPTY
             } else {
                 C.STR_BANG_BANG
             }
 
             C.TEMPLATE_WRAP_TYPE.format(typeName, "$ctxVar$bang")
         }
 
         else -> {
             if (isNullable) {
                 ctxVar
             } else {
                 "$ctxVar${C.STR_BANG_BANG}"
             }
         }
     }
 }
 
 /**
  * Generates the conditional fallback return expression when fragmented deserialization
  * supports default arguments. It maps tracking mask checks directly to the fields on
  * the `DecodingContext` instance.
  *
  * @param maskIdx Index of the tracking bitmask inside `DecodingContext`.
  * @param bitMaskStr String representation of the bitmask representing this property.
  */
 internal fun GhostPropertyModel.getFragmentedDefaultValueReturnExpression(
     maskIdx: Int,
     bitMaskStr: String
 ): String {
 
     val isPrimitive = type.isPrimitive() && !isNullable
     val isUnboxedValueClass = isValueClass && valueClassProperty != null && !isNullable
 
     val maskName = C.TEMPLATE_CTX_MASK_VAR.format(maskIdx)
     val ctxVar = C.TEMPLATE_CTX_VAR.format(kotlinName)
     val resultVar = C.TEMPLATE_RESULT_VAR.format(kotlinName)
 
     return when {
         isNullable || isPrimitive ->
             C.TEMPLATE_IF_MASK_RETURN.format(maskName, bitMaskStr, ctxVar, resultVar)
 
         isUnboxedValueClass -> {
             val bang = if (valueClassProperty!!.type.isPrimitive()) {
                 C.STR_EMPTY
             } else {
                 C.STR_BANG_BANG
             }
             C.TEMPLATE_WRAP_TYPE.format(
                 typeName,
                 C.TEMPLATE_IF_MASK_RETURN.format(
                     maskName,
                     bitMaskStr,
                     "$ctxVar$bang",
                     resultVar
                 )
 
             )
         }
 
         else -> {
             val value = "$ctxVar${C.STR_BANG_BANG}"
             C.TEMPLATE_IF_MASK_RETURN.format(
                 maskName,
                 bitMaskStr,
                 value,
                 resultVar
             )
         }
     }
 }