From ffe4dfee9a482836c10833c4077dcb139d4a1e75 Mon Sep 17 00:00:00 2001 From: Jaya Surya Thotapalli Date: Wed, 6 Mar 2024 21:11:39 +0530 Subject: [PATCH] (#19) Add support for immutable collections --- .../dowel/processor/DowelSymbolProcessor.kt | 15 ++- .../com/jayasuryat/dowel/processor/Names.kt | 15 +++ .../processor/generator/DowelGenerator.kt | 13 ++ .../processor/generator/ObjectConstructor.kt | 120 ++++++++++++++++-- .../processor/model/ClassRepresentation.kt | 16 +++ 5 files changed, 165 insertions(+), 14 deletions(-) diff --git a/dowel-processor/src/main/java/com/jayasuryat/dowel/processor/DowelSymbolProcessor.kt b/dowel-processor/src/main/java/com/jayasuryat/dowel/processor/DowelSymbolProcessor.kt index 3bb8cae..f6203c4 100644 --- a/dowel-processor/src/main/java/com/jayasuryat/dowel/processor/DowelSymbolProcessor.kt +++ b/dowel-processor/src/main/java/com/jayasuryat/dowel/processor/DowelSymbolProcessor.kt @@ -25,6 +25,7 @@ import com.jayasuryat.dowel.annotation.Dowel import com.jayasuryat.dowel.annotation.DowelList import com.jayasuryat.dowel.processor.generator.DowelGenerator import com.jayasuryat.dowel.processor.generator.DowelListGenerator +import com.jayasuryat.dowel.processor.model.PreDefinedDeclarations import com.jayasuryat.dowel.processor.model.UserPredefinedParamProviderMapper import com.jayasuryat.dowel.processor.model.UserPredefinedParamProviderMapper.ProcessedConsiderForDowelSymbols import com.jayasuryat.dowel.processor.model.UserPredefinedParamProviders @@ -69,6 +70,11 @@ internal class DowelSymbolProcessor( codeGenerator = codeGenerator, ) } + private val declarations: PreDefinedDeclarations by unsafeLazy { + PreDefinedDeclarations( + resolver = resolver, + ) + } /** * Entry point into processing of symbols, called by Kotlin Symbol Processing to run the processing task. @@ -81,7 +87,8 @@ internal class DowelSymbolProcessor( resolver.processConsiderForDowelSymbols() val invalidDowelSymbols: List = resolver.processDowelSymbols( - predefinedProviders = considerForDowelSymbols.providers + predefinedProviders = considerForDowelSymbols.providers, + declarations = declarations, ) val invalidDowelListSymbols: List = resolver.processDowelListSymbols() @@ -95,6 +102,7 @@ internal class DowelSymbolProcessor( */ private fun Resolver.processDowelSymbols( predefinedProviders: UserPredefinedParamProviders, + declarations: PreDefinedDeclarations, ): List { val resolver = this @@ -108,6 +116,7 @@ internal class DowelSymbolProcessor( val dowelGenerator: DowelGenerator = DowelGenerator.createInstance( predefinedProviders = predefinedProviders, + declarations = declarations, ) // Triggering code generation for valid symbols @@ -169,12 +178,14 @@ internal class DowelSymbolProcessor( */ private fun DowelGenerator.Companion.createInstance( predefinedProviders: UserPredefinedParamProviders, + declarations: PreDefinedDeclarations, ): DowelGenerator { return DowelGenerator( resolver = resolver, codeGenerator = codeGenerator, logger = logger, - predefinedProviders = predefinedProviders + predefinedProviders = predefinedProviders, + declarations = declarations, ) } } diff --git a/dowel-processor/src/main/java/com/jayasuryat/dowel/processor/Names.kt b/dowel-processor/src/main/java/com/jayasuryat/dowel/processor/Names.kt index 4773d26..b9068de 100644 --- a/dowel-processor/src/main/java/com/jayasuryat/dowel/processor/Names.kt +++ b/dowel-processor/src/main/java/com/jayasuryat/dowel/processor/Names.kt @@ -59,6 +59,21 @@ internal object Names { "MutableMap" ) + val persistentList: ClassName = ClassName( + packageName = "kotlinx.collections.immutable", + "PersistentList", + ) + + val persistentSet: ClassName = ClassName( + packageName = "kotlinx.collections.immutable", + "PersistentSet", + ) + + val persistentMap: ClassName = ClassName( + packageName = "kotlinx.collections.immutable", + "PersistentMap", + ) + val sequenceName: ClassName = Sequence::class.asTypeName() val previewParamProvider: ClassName = ClassName( diff --git a/dowel-processor/src/main/java/com/jayasuryat/dowel/processor/generator/DowelGenerator.kt b/dowel-processor/src/main/java/com/jayasuryat/dowel/processor/generator/DowelGenerator.kt index b80c06f..5b5c876 100644 --- a/dowel-processor/src/main/java/com/jayasuryat/dowel/processor/generator/DowelGenerator.kt +++ b/dowel-processor/src/main/java/com/jayasuryat/dowel/processor/generator/DowelGenerator.kt @@ -28,6 +28,7 @@ import com.jayasuryat.dowel.processor.model.ClassRepresentation import com.jayasuryat.dowel.processor.model.ClassRepresentation.ParameterSpec.DowelSpec import com.jayasuryat.dowel.processor.model.ClassRepresentation.ParameterSpec.PreDefinedProviderSpec import com.jayasuryat.dowel.processor.model.ClassRepresentationMapper +import com.jayasuryat.dowel.processor.model.PreDefinedDeclarations import com.jayasuryat.dowel.processor.model.UserPredefinedParamProviders import com.jayasuryat.dowel.processor.util.getEffectiveModuleVisibility import com.jayasuryat.dowel.processor.util.unsafeLazy @@ -54,6 +55,7 @@ internal class DowelGenerator( private val codeGenerator: CodeGenerator, private val logger: KSPLogger, private val predefinedProviders: UserPredefinedParamProviders, + private val declarations: PreDefinedDeclarations, ) { private val mapper: ClassRepresentationMapper by unsafeLazy { @@ -61,6 +63,7 @@ internal class DowelGenerator( resolver = resolver, logger = logger, predefinedProviders = predefinedProviders, + declarations = declarations, ) } private val objectConstructor: ObjectConstructor by unsafeLazy { ObjectConstructor() } @@ -265,6 +268,16 @@ internal class DowelGenerator( spec.keySpec.getAllSupportingProvidersRecursively() + spec.valueSpec.getAllSupportingProvidersRecursively() + is ClassRepresentation.ParameterSpec.PersistentListSpec -> + spec.elementSpec.getAllSupportingProvidersRecursively() + + is ClassRepresentation.ParameterSpec.PersistentSetSpec -> + spec.elementSpec.getAllSupportingProvidersRecursively() + + is ClassRepresentation.ParameterSpec.PersistentMapSpec -> + spec.keySpec.getAllSupportingProvidersRecursively() + + spec.valueSpec.getAllSupportingProvidersRecursively() + is ClassRepresentation.ParameterSpec.FlowSpec -> spec.elementSpec.getAllSupportingProvidersRecursively() diff --git a/dowel-processor/src/main/java/com/jayasuryat/dowel/processor/generator/ObjectConstructor.kt b/dowel-processor/src/main/java/com/jayasuryat/dowel/processor/generator/ObjectConstructor.kt index 3e702c6..2d4b7e8 100644 --- a/dowel-processor/src/main/java/com/jayasuryat/dowel/processor/generator/ObjectConstructor.kt +++ b/dowel-processor/src/main/java/com/jayasuryat/dowel/processor/generator/ObjectConstructor.kt @@ -70,7 +70,10 @@ internal class ObjectConstructor { } /** - * Returns [CodeBlock] representing assignment logic for the passed [ClassRepresentation.ParameterSpec] + * Returns [CodeBlock] representing assignment logic for the passed [ClassRepresentation.ParameterSpec]. + * + * Note: For [SealedSpec], and all the other types which have generic type parameters, + * this method would get called recursively */ private fun ClassRepresentation.ParameterSpec.getAssigner(): CodeBlock { @@ -84,14 +87,19 @@ internal class ObjectConstructor { is BooleanSpec -> spec.getBoolAssigner() is StringSpec -> spec.getStringAssigner() - is ListSpec -> spec.getListAssigner() // This would be a recursive call - is SetSpec -> spec.getSetAssigner() // This would be a recursive call - is MapSpec -> spec.getMapAssigner() // This would be a recursive call - is FlowSpec -> spec.getFlowAssigner() // This would be a recursive call - is PairSpec -> spec.getPairAssigner() // This would be a recursive call + is ListSpec -> spec.getListAssigner() + is SetSpec -> spec.getSetAssigner() + is MapSpec -> spec.getMapAssigner() + + is PersistentListSpec -> spec.getPersistentListSpecAssigner() + is PersistentSetSpec -> spec.getPersistentSetAssigner() + is PersistentMapSpec -> spec.getPersistentMapAssigner() + + is FlowSpec -> spec.getFlowAssigner() + is PairSpec -> spec.getPairAssigner() is FunctionSpec -> spec.getFunctionAssigner() - is SealedSpec -> spec.getSealedAssigner() // This would be a recursive call + is SealedSpec -> spec.getSealedAssigner() is EnumSpec -> spec.getEnumAssigner() is ObjectSpec -> spec.getObjectAssigner() is DowelSpec -> spec.getDowelAssigner() @@ -101,7 +109,7 @@ internal class ObjectConstructor { is NoArgsConstructorSpec -> spec.getNoArgsConstructorAssigner() // Compose types - is StateSpec -> spec.getStateAssigner() // This would be a recursive call + is StateSpec -> spec.getStateAssigner() is ColorSpec -> spec.getColorAssigner() is UnsupportedNullableSpec -> spec.getUnsupportedNullableAssigner() @@ -147,13 +155,13 @@ internal class ObjectConstructor { return buildCodeBlock { add("%L", value) } } - @Suppress("unused") + @Suppress("UnusedReceiverParameter") private fun CharSpec.getCharAssigner(): CodeBlock { val range = 'a'..'z' return buildCodeBlock { add("\'%L\'", range.random()) } } - @Suppress("unused") + @Suppress("UnusedReceiverParameter") private fun BooleanSpec.getBoolAssigner(): CodeBlock { return buildCodeBlock { add("%L", Random.nextBoolean()) } } @@ -267,6 +275,94 @@ internal class ObjectConstructor { } } + private fun PersistentListSpec.getPersistentListSpecAssigner(): CodeBlock { + + val spec = this + + val listSize = spec.size.value.toSafeRangeInt() + val persistentListOf = MemberName("kotlinx.collections.immutable", "persistentListOf") + + if (listSize == 0) { + return buildCodeBlock { add("%M()", persistentListOf) } + } + + val modListSize: Int = if (listSize != -1) listSize + else Random.nextLong( + from = spec.size.min, + until = spec.size.max, + ).toSafeRangeInt() + + return buildCodeBlock { + add("%M(\n", persistentListOf) + withIndent { + repeat(modListSize) { + add("%L,\n", spec.elementSpec.getAssigner()) + } + } + add(")") + } + } + + private fun PersistentSetSpec.getPersistentSetAssigner(): CodeBlock { + + val spec = this + + val setSize = spec.size.value.toSafeRangeInt() + val persistentSetOf = MemberName("kotlinx.collections.immutable", "persistentSetOf") + + if (setSize == 0) { + return buildCodeBlock { add("%M()", persistentSetOf) } + } + + val modSetSize: Int = if (setSize != -1) setSize + else Random.nextLong( + from = spec.size.min, + until = spec.size.max, + ).toSafeRangeInt() + + return buildCodeBlock { + add("%M(\n", persistentSetOf) + withIndent { + repeat(modSetSize) { + add("%L,\n", spec.elementSpec.getAssigner()) + } + } + add(")") + } + } + + private fun PersistentMapSpec.getPersistentMapAssigner(): CodeBlock { + + val spec = this + + val listSize = spec.size.value.toSafeRangeInt() + val persistentMapOf = MemberName("kotlinx.collections.immutable", "persistentMapOf") + + if (listSize == 0) { + return buildCodeBlock { add("%M()", persistentMapOf) } + } + + val modSize: Int = if (listSize != -1) listSize + else Random.nextLong( + from = spec.size.min, + until = spec.size.max, + ).toSafeRangeInt() + + return buildCodeBlock { + add("%M(\n", persistentMapOf) + withIndent { + repeat(modSize) { + add( + "%L to %L,\n", + spec.keySpec.getAssigner(), + spec.valueSpec.getAssigner(), + ) + } + } + add(")") + } + } + private fun FlowSpec.getFlowAssigner(): CodeBlock { val spec = this @@ -369,7 +465,7 @@ internal class ObjectConstructor { } } - @Suppress("unused") + @Suppress("UnusedReceiverParameter") private fun ColorSpec.getColorAssigner(): CodeBlock { return buildCodeBlock { @@ -380,7 +476,7 @@ internal class ObjectConstructor { } } - @Suppress("unused") + @Suppress("UnusedReceiverParameter") private fun UnsupportedNullableSpec.getUnsupportedNullableAssigner(): CodeBlock { return buildCodeBlock { add("null") } } diff --git a/dowel-processor/src/main/java/com/jayasuryat/dowel/processor/model/ClassRepresentation.kt b/dowel-processor/src/main/java/com/jayasuryat/dowel/processor/model/ClassRepresentation.kt index 7ef7ae4..dd6d1a6 100644 --- a/dowel-processor/src/main/java/com/jayasuryat/dowel/processor/model/ClassRepresentation.kt +++ b/dowel-processor/src/main/java/com/jayasuryat/dowel/processor/model/ClassRepresentation.kt @@ -97,6 +97,22 @@ internal data class ClassRepresentation( val valueSpec: ParameterSpec, ) : ParameterSpec + data class PersistentListSpec( + val size: Size, + val elementSpec: ParameterSpec, + ) : ParameterSpec + + data class PersistentSetSpec( + val size: Size, + val elementSpec: ParameterSpec, + ) : ParameterSpec + + data class PersistentMapSpec( + val size: Size, + val keySpec: ParameterSpec, + val valueSpec: ParameterSpec, + ) : ParameterSpec + data class FlowSpec( val elementSpec: ParameterSpec, ) : ParameterSpec