diff --git a/README.md b/README.md index f68e1d6..39ff021 100644 --- a/README.md +++ b/README.md @@ -219,6 +219,7 @@ Apart from that if you want to controll range / legnth / size of the values bein - Kotlin Objects - `Enum` - `List` + - `Set` - `Map` - `Pair` - Nullable types @@ -226,7 +227,7 @@ Apart from that if you want to controll range / legnth / size of the values bein - Properties with default values can have *any type*, as they are not considered while generating code - Types in the above mentioned list having generic type parameters (like `List` and `Map`) can only have `@Dowel` supported types as their type parameters. Like `List`, `Map` - As far as a type is in above mentioned supported list, there are no practical limitations on how many times they may be nested. -Like `List>>` +Like `List, List<@Dowel class>>>` ## `Dowel` ships with `lint` rules `Dowel` ships with `lint` rules which cover all of the basic validation scenarios, and it will warn you even before you might compile the code if any improper usage is detected. diff --git a/app/src/main/java/com/jayasuryat/dowel/sample/ui/home/model/Person.kt b/app/src/main/java/com/jayasuryat/dowel/sample/ui/home/model/Person.kt index cad7a3c..6a7e868 100644 --- a/app/src/main/java/com/jayasuryat/dowel/sample/ui/home/model/Person.kt +++ b/app/src/main/java/com/jayasuryat/dowel/sample/ui/home/model/Person.kt @@ -47,8 +47,10 @@ data class Person( val meta: SomeStaticInfo, val customType: UnsupportedType = UnsupportedType.SomeType, @Size(value = 2) val locations: List, + @Size(value = 3) val uniqueLocations: Set, val isExpanded: State, @Size(value = 1) val preferences: Map, + @Size(value = 2) val preferredLocations: Map>, val title: Char, @Size(value = 1) val interests: List, val onClick: suspend (a: Person, b: Int) -> Unit, 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 b12a4a4..a0a1fd5 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 @@ -261,6 +261,9 @@ internal class DowelGenerator( is ClassRepresentation.ParameterSpec.ListSpec -> spec.elementSpec.getAllSupportingProvidersRecursively() + is ClassRepresentation.ParameterSpec.SetSpec -> + spec.elementSpec.getAllSupportingProvidersRecursively() + is ClassRepresentation.ParameterSpec.MapSpec -> spec.keySpec.getAllSupportingProvidersRecursively() + spec.valueSpec.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 abe6947..8c96a28 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 @@ -85,6 +85,7 @@ internal class ObjectConstructor { is StateSpec -> spec.getStateAssigner() // This would be a recursive call 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 @@ -214,6 +215,33 @@ internal class ObjectConstructor { } } + private fun SetSpec.getSetAssigner(): CodeBlock { + + val spec = this + + val setSize = spec.size.value.toSafeRangeInt() + + if (setSize == 0) { + return buildCodeBlock { add("setOf()") } + } + + val modSetSize: Int = if (setSize != -1) setSize + else Random.nextLong( + from = spec.size.min, + until = spec.size.max, + ).toSafeRangeInt() + + return buildCodeBlock { + add("setOf(\n") + withIndent { + repeat(modSetSize) { + add("%L,\n", spec.elementSpec.getAssigner()) + } + } + add(")") + } + } + private fun MapSpec.getMapAssigner(): CodeBlock { val spec = this 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 3ab7343..b39ff06 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 @@ -90,6 +90,11 @@ internal data class ClassRepresentation( val elementSpec: ParameterSpec, ) : ParameterSpec + data class SetSpec( + val size: Size, + val elementSpec: ParameterSpec, + ) : ParameterSpec + data class MapSpec( val size: Size, val keySpec: ParameterSpec, diff --git a/dowel-processor/src/main/java/com/jayasuryat/dowel/processor/model/ClassRepresentationMapper.kt b/dowel-processor/src/main/java/com/jayasuryat/dowel/processor/model/ClassRepresentationMapper.kt index e35898a..ccd8e69 100644 --- a/dowel-processor/src/main/java/com/jayasuryat/dowel/processor/model/ClassRepresentationMapper.kt +++ b/dowel-processor/src/main/java/com/jayasuryat/dowel/processor/model/ClassRepresentationMapper.kt @@ -76,6 +76,10 @@ internal class ClassRepresentationMapper( val ksName = resolver.getKSNameFromString(List::class.qualifiedName!!) resolver.getClassDeclarationByName(ksName)!!.asStarProjectedType() } + private val setDeclaration: KSType by unsafeLazy { + val ksName = resolver.getKSNameFromString(Set::class.qualifiedName!!) + resolver.getClassDeclarationByName(ksName)!!.asStarProjectedType() + } private val mapDeclaration: KSType by unsafeLazy { val ksName = resolver.getKSNameFromString(Map::class.qualifiedName!!) resolver.getClassDeclarationByName(ksName)!!.asStarProjectedType() @@ -175,6 +179,9 @@ internal class ClassRepresentationMapper( // List listDeclaration.isAssignableFrom(propType) -> propType.getListSpec(annotations) + // Set + setDeclaration.isAssignableFrom(propType) -> propType.getSetSpec(annotations) + // Map mapDeclaration.isAssignableFrom(propType) -> propType.getMapSpec(annotations) @@ -369,6 +376,33 @@ internal class ClassRepresentationMapper( } } + private fun KSType.getSetSpec( + annotations: List, + ): MaybeSpec { + + val size: Size = Size.find( + annotations = annotations.toList(), + defaultValue = DefaultRange.DEFAULT_LIST_LEN_VALUE, + defaultMin = DefaultRange.DEFAULT_LIST_LEN_MIN, + defaultMax = DefaultRange.DEFAULT_LIST_LEN_MAX, + ) + + require(this.arguments.size == 1) { "Set must have have exactly one type argument. Current size = ${this.arguments.size}" } + + val arg = this.arguments.first() + val resolvedType = arg.type!!.resolve() + val spec = resolvedType.getSpec( + annotations = arg.annotations.toList(), + ) + + return either { + SetSpec( + size = size, + elementSpec = spec.bind(), + ) + } + } + private fun KSType.getMapSpec( annotations: List, ): MaybeSpec { diff --git a/dowel-processor/src/test/java/com/jayasuryat/dowel/processor/DowelWholeEnchiladaProcessingTest.kt b/dowel-processor/src/test/java/com/jayasuryat/dowel/processor/DowelWholeEnchiladaProcessingTest.kt index f43bd9f..ebf2ee3 100644 --- a/dowel-processor/src/test/java/com/jayasuryat/dowel/processor/DowelWholeEnchiladaProcessingTest.kt +++ b/dowel-processor/src/test/java/com/jayasuryat/dowel/processor/DowelWholeEnchiladaProcessingTest.kt @@ -208,10 +208,13 @@ internal class DowelWholeEnchiladaProcessingTest { val latLon: Pair, val info: SomeStaticInfo, @Size(value = 2) val locations: List, + @Size(value = 3) val uniqueLocations: Set, val isExpanded: State, @Size(value = 1) val preferences: Map, + @Size(value = 2) val preferredLocations: Map>, val title: Char, @Size(value = 1) val interests: List, + @Size(value = 2) val uniqueInterests: Set, val meta : MetaInfo, val subjects : Subjects, val onClick: suspend (a: Person, b: Int) -> Unit,