diff --git a/README.md b/README.md index 7cbcf0f..788065b 100644 --- a/README.md +++ b/README.md @@ -25,7 +25,9 @@ buildConfig { buildConfigField("FEATURE_ENABLED", true) buildConfigField("MAGIC_NUMBERS", intArrayOf(1, 2, 3, 4)) buildConfigField("STRING_LIST", arrayOf("a", "b", "c")) - buildConfigField>("MAP", expression("mapOf(\"a\" to 1, \"b\" to 2)")) + buildConfigField("MAP", mapOf("a" to 1, "b" to 2)) + buildConfigField("FILE", File("aFile")) + buildConfigField("URI", uri("https://example.io")) buildConfigField("com.github.gmazzo.buildconfig.demos.kts.SomeData", "DATA", "SomeData(\"a\", 1)") } @@ -43,6 +45,8 @@ internal object BuildConfig { internal val MAGIC_NUMBERS: IntArray = intArrayOf(1, 2, 3) internal val STRING_LIST: Array = arrayOf("a", "b", "c") internal val MAP: Map = mapOf("a" to 1, "b" to 2) + internal val FILE: File = java.io.File("aFile") + internal val URI: URI = java.net.URI.create("https://example.io") internal val DATA: SomeData = SomeData("a", 1) } ``` @@ -64,8 +68,9 @@ buildConfig { buildConfigField(boolean, 'FEATURE_ENABLED', true) buildConfigField(int[], "MAGIC_NUMBERS", [1, 2, 3]) buildConfigField('List', "STRING_LIST", ["a", "b", "c"]) - buildConfigField("java.util.Map", "MAP", "java.util.Map.of(\"a\", 1, \"b\", 2)") - buildConfigField(Map.class, "GENERIC_MAP", expression("java.util.Map.of(\"a\", 1, \"b\", 2)")) + buildConfigField(Map.class, "MAP", [a: 1, b: 2]) + buildConfigField(File.class, "FILE", new File("aFile")) + buildConfigField(URI.class, "URI", uri("https://example.io")) buildConfigField("com.github.gmazzo.buildconfig.demos.groovy.SomeData", "DATA", "new SomeData(\"a\", 1)") } ``` @@ -82,6 +87,8 @@ final class BuildConfig { public static final int[] MAGIC_NUMBERS = {1, 2, 3}; public static final String[] STRING_LIST = {"a", "b", "c"}; public static final Map MAP = java.util.Map.of("a", 1, "b", 2); + public static final File FILE = new java.io.File("aFile"); + public static final URI URI = java.net.URI.create("https://example.io"); public static final SomeData DATA = new SomeData("a", 1); } ``` diff --git a/demo-project/groovy-gen-kotlin/build.gradle b/demo-project/groovy-gen-kotlin/build.gradle index 28dc6df..6cb18bd 100644 --- a/demo-project/groovy-gen-kotlin/build.gradle +++ b/demo-project/groovy-gen-kotlin/build.gradle @@ -160,14 +160,16 @@ buildConfig { buildConfigField('Set', "BOOLEAN_SET_PROVIDER", provider { [true, null, false] }) // custom formats with expressions, including Map and custom types + buildConfigField(Map.class, "MAP", [a: 1, b: 2]) + buildConfigField(Map.class, "MAP_PROVIDER", [a: 1, b: 2]) buildConfigField( "Map", - "MAP", + "MAP_BY_EXPRESSION", "mapOf(\"a\" to 1, \"b\" to 2)" ) buildConfigField( "Map", - "MAP_PROVIDER", + "MAP_BY_EXPRESSION_PROVIDER", provider { "mapOf(\"a\" to 1, \"b\" to 2)" } ) buildConfigField(Map.class, "MAP_GENERIC", expression("mapOf(\"a\" to 1, \"b\" to 2)")) diff --git a/demo-project/groovy/build.gradle b/demo-project/groovy/build.gradle index 5993326..dd0223c 100644 --- a/demo-project/groovy/build.gradle +++ b/demo-project/groovy/build.gradle @@ -160,14 +160,16 @@ buildConfig { buildConfigField('Set', "BOOLEAN_SET_PROVIDER", provider { [true, null, false] }) // custom formats with expressions, including Map and custom types + buildConfigField(Map.class, "MAP", [a: 1, b: 2]) + buildConfigField(Map.class, "MAP_PROVIDER", [a: 1, b: 2]) buildConfigField( "java.util.Map", - "MAP", + "MAP_BY_EXPRESSION", "java.util.Map.of(\"a\", 1, \"b\", 2)" ) buildConfigField( "java.util.Map", - "MAP_PROVIDER", + "MAP_BY_EXPRESSION_PROVIDER", provider { "java.util.Map.of(\"a\", 1, \"b\", 2)" } ) buildConfigField(Map.class, "MAP_GENERIC", expression("java.util.Map.of(\"a\", 1, \"b\", 2)")) diff --git a/demo-project/groovy/src/testFixtures/java/com/github/gmazzo/buildconfig/demos/groovy/BuildConfigBaseTest.java b/demo-project/groovy/src/testFixtures/java/com/github/gmazzo/buildconfig/demos/groovy/BuildConfigBaseTest.java index 801c9c7..b92a9ee 100644 --- a/demo-project/groovy/src/testFixtures/java/com/github/gmazzo/buildconfig/demos/groovy/BuildConfigBaseTest.java +++ b/demo-project/groovy/src/testFixtures/java/com/github/gmazzo/buildconfig/demos/groovy/BuildConfigBaseTest.java @@ -178,6 +178,8 @@ public void testBooleans() { public void testCustomTypes() { assertEquals(Map.of("a", 1, "b", 2), BuildConfig.MAP); assertEquals(Map.of("a", 1, "b", 2), BuildConfig.MAP_PROVIDER); + assertEquals(Map.of("a", 1, "b", 2), BuildConfig.MAP_BY_EXPRESSION); + assertEquals(Map.of("a", 1, "b", 2), BuildConfig.MAP_BY_EXPRESSION_PROVIDER); assertEquals(Map.of("a", 1, "b", 2), BuildConfig.MAP_GENERIC); assertEquals(Map.of("a", 1, "b", 2), BuildConfig.MAP_GENERIC_PROVIDER); assertEquals(new File("aFile"), BuildConfig.FILE); diff --git a/demo-project/kts-gen-java/build.gradle.kts b/demo-project/kts-gen-java/build.gradle.kts index acecb59..fe94634 100644 --- a/demo-project/kts-gen-java/build.gradle.kts +++ b/demo-project/kts-gen-java/build.gradle.kts @@ -157,8 +157,10 @@ buildConfig { buildConfigField("BOOLEAN_SET_PROVIDER", provider { setOf(true, null, false) }) // custom formats with expressions, including Map and custom types - buildConfigField>("MAP", expression("java.util.Map.of(\"a\", 1, \"b\", 2)")) - buildConfigField>("MAP_PROVIDER", provider { expression("java.util.Map.of(\"a\", 1, \"b\", 2)") }) + buildConfigField("MAP", mapOf("a" to 1, "b" to 2)) + buildConfigField("MAP_PROVIDER", provider { mapOf("a" to 1, "b" to 2) }) + buildConfigField>("MAP_BY_EXPRESSION", expression("java.util.Map.of(\"a\", 1, \"b\", 2)")) + buildConfigField>("MAP_BY_EXPRESSION_PROVIDER", provider { expression("java.util.Map.of(\"a\", 1, \"b\", 2)") }) buildConfigField>("MAP_GENERIC", expression("java.util.Map.of(\"a\", 1, \"b\", 2)")) buildConfigField>("MAP_GENERIC_PROVIDER", provider { expression("java.util.Map.of(\"a\", 1, \"b\", 2)") }) buildConfigField("FILE", File("aFile")) diff --git a/demo-project/kts/build.gradle.kts b/demo-project/kts/build.gradle.kts index 91a4789..c7245a3 100644 --- a/demo-project/kts/build.gradle.kts +++ b/demo-project/kts/build.gradle.kts @@ -172,8 +172,10 @@ buildConfig { buildConfigField("BOOLEAN_SET_PROVIDER", provider { setOf(true, null, false) }) // custom formats with expressions, including Map and custom types - buildConfigField>("MAP", expression("mapOf(\"a\" to 1, \"b\" to 2)")) - buildConfigField>("MAP_PROVIDER", provider { expression("mapOf(\"a\" to 1, \"b\" to 2)") }) + buildConfigField("MAP", mapOf("a" to 1, "b" to 2)) + buildConfigField("MAP_PROVIDER", provider { mapOf("a" to 1, "b" to 2) }) + buildConfigField>("MAP_BY_EXPRESSION", expression("mapOf(\"a\" to 1, \"b\" to 2)")) + buildConfigField>("MAP_BY_EXPRESSION_PROVIDER", provider { expression("mapOf(\"a\" to 1, \"b\" to 2)") }) buildConfigField>("MAP_GENERIC", expression("mapOf(\"a\" to 1, \"b\" to 2)")) buildConfigField>("MAP_GENERIC_PROVIDER", provider { expression("mapOf(\"a\" to 1, \"b\" to 2)") }) buildConfigField("FILE", File("aFile")) diff --git a/plugin/src/main/kotlin/com/github/gmazzo/buildconfig/BuildConfigTypeUtils.kt b/plugin/src/main/kotlin/com/github/gmazzo/buildconfig/BuildConfigTypeUtils.kt index c52b153..b4655f2 100644 --- a/plugin/src/main/kotlin/com/github/gmazzo/buildconfig/BuildConfigTypeUtils.kt +++ b/plugin/src/main/kotlin/com/github/gmazzo/buildconfig/BuildConfigTypeUtils.kt @@ -143,6 +143,7 @@ internal val Any?.elements: List is DoubleArray -> toList() is BooleanArray -> toList() is Collection<*> -> toList() + is Map<*, *> -> entries.toList() else -> listOf(this) } @@ -158,5 +159,6 @@ internal fun Any?.asVarArg(): Array<*> = when (this) { is BooleanArray -> toTypedArray() is List<*> -> toTypedArray() is Iterable<*> -> toList().toTypedArray() + is Map<*, *> -> entries.asSequence().flatMap { (k, v) -> sequenceOf(k, v) }.toList().toTypedArray() else -> arrayOf(this) } diff --git a/plugin/src/main/kotlin/com/github/gmazzo/buildconfig/generators/BuildConfigJavaGenerator.kt b/plugin/src/main/kotlin/com/github/gmazzo/buildconfig/generators/BuildConfigJavaGenerator.kt index 4d1b08c..b604aa8 100644 --- a/plugin/src/main/kotlin/com/github/gmazzo/buildconfig/generators/BuildConfigJavaGenerator.kt +++ b/plugin/src/main/kotlin/com/github/gmazzo/buildconfig/generators/BuildConfigJavaGenerator.kt @@ -129,11 +129,11 @@ data class BuildConfigJavaGenerator( else -> "\$L" } - fun List.format(prefix: String, postfix: String, item: (Any) -> TypeName) = joinToString( + fun List.format(prefix: String, postfix: String, elementType: TypeName?) = joinToString( prefix = prefix, separator = ", ", postfix = postfix, - transform = { it?.let(item).format() } + transform = { it?.let { elementType ?: TypeName.get(it::class.java) }.format() } ) to size val elements = forValue.elements @@ -141,23 +141,38 @@ data class BuildConfigJavaGenerator( fun singleFormat() = elements.single()?.let { TypeName.get(it::class.java) }.format() to 1 - fun arrayFormat(item: (Any) -> TypeName) = - elements.format("{", "}", item) + fun listFormat(elementType: TypeName?) = + elements.format("java.util.Arrays.asList(", ")", elementType) - fun listFormat(item: (Any) -> TypeName) = - elements.format("java.util.Arrays.asList(", ")", item) + fun setFormat(elementType: TypeName?) = + elements.format("new java.util.LinkedHashSet<>(java.util.Arrays.asList(", "))", elementType) - fun setFormat(item: (Any) -> TypeName) = - elements.format("new java.util.LinkedHashSet<>(java.util.Arrays.asList(", "))", item) + fun mapFormat(keyType: TypeName?, valueType: TypeName?) = + elements.joinToString( + prefix = "java.util.Map.of(", + separator = ", ", + postfix = ")", + transform = { + val (key, value) = (it as Map.Entry) + val keyFormat = (keyType ?: key?.let { TypeName.get(key::class.java) }).format() + val valueFormat = (valueType ?: value?.let { TypeName.get(value::class.java) }).format() + + "$keyFormat, $valueFormat" + } + ) to elements.size * 2 return when (this) { TypeName.LONG, ClassName.get(String::class.java) -> singleFormat() - is ArrayTypeName -> arrayFormat { componentType } - LIST, GENERIC_LIST -> listFormat { TypeName.get(it::class.java) } - SET, GENERIC_SET -> setFormat { TypeName.get(it::class.java) } + is ArrayTypeName ->elements.format("{", "}", componentType) + LIST, GENERIC_LIST -> listFormat(null) + SET, GENERIC_SET -> setFormat(null) + MAP, GENERIC_MAP -> mapFormat(null, null) is ParameterizedTypeName -> when (rawType) { - LIST, GENERIC_LIST -> listFormat { typeArguments.first() } - SET, GENERIC_SET -> setFormat { typeArguments.first() } + LIST, GENERIC_LIST -> listFormat(typeArguments[0].takeIf { it.isBoxedPrimitive }) + SET, GENERIC_SET -> setFormat(typeArguments[0].takeIf { it.isBoxedPrimitive }) + MAP, GENERIC_MAP -> mapFormat( + typeArguments[0].takeIf { it.isBoxedPrimitive }, + typeArguments[1].takeIf { it.isBoxedPrimitive }) else -> singleFormat() } @@ -169,10 +184,12 @@ data class BuildConfigJavaGenerator( private val STRING = ClassName.get(String::class.java) private val LIST = ClassName.get(List::class.java) private val SET = ClassName.get(Set::class.java) + private val MAP = ClassName.get(Map::class.java) private val FILE = ClassName.get(File::class.java) private val URI = ClassName.get(URI::class.java) private val GENERIC_LIST = ClassName.get("", "List") private val GENERIC_SET = ClassName.get("", "Set") + private val GENERIC_MAP = ClassName.get("", "Map") } } diff --git a/plugin/src/main/kotlin/com/github/gmazzo/buildconfig/generators/BuildConfigKotlinGenerator.kt b/plugin/src/main/kotlin/com/github/gmazzo/buildconfig/generators/BuildConfigKotlinGenerator.kt index cb54c7a..79d306b 100644 --- a/plugin/src/main/kotlin/com/github/gmazzo/buildconfig/generators/BuildConfigKotlinGenerator.kt +++ b/plugin/src/main/kotlin/com/github/gmazzo/buildconfig/generators/BuildConfigKotlinGenerator.kt @@ -24,6 +24,7 @@ import com.squareup.kotlinpoet.KModifier import com.squareup.kotlinpoet.LIST import com.squareup.kotlinpoet.LONG import com.squareup.kotlinpoet.LONG_ARRAY +import com.squareup.kotlinpoet.MAP import com.squareup.kotlinpoet.ParameterizedTypeName import com.squareup.kotlinpoet.ParameterizedTypeName.Companion.parameterizedBy import com.squareup.kotlinpoet.PropertySpec @@ -149,11 +150,11 @@ data class BuildConfigKotlinGenerator( else -> "%L" } - fun List.format(function: String, item: (Any) -> TypeName) = joinToString( + fun List.format(function: String, elementType: TypeName?) = joinToString( prefix = "$function(", separator = ", ", postfix = ")", - transform = { it?.let(item).format() } + transform = { it?.let { elementType ?: it::class.asTypeName() }.format() } ) to size val elements = forValue.elements @@ -161,32 +162,48 @@ data class BuildConfigKotlinGenerator( fun singleFormat() = elements.single()?.let { it::class.asTypeName() }.format() to 1 - fun arrayFormat(item: (Any) -> TypeName) = - elements.format("arrayOf", item) + fun arrayFormat(elementType: TypeName?) = + elements.format("arrayOf", elementType) - fun listFormat(item: (Any) -> TypeName) = - elements.format("listOf", item) + fun listFormat(elementType: TypeName?) = + elements.format("listOf", elementType) - fun setFormat(item: (Any) -> TypeName) = - elements.format("setOf", item) + fun setFormat(elementType: TypeName?) = + elements.format("setOf", elementType) + + fun mapFormat(keyType: TypeName?, valueType: TypeName?) = + elements.joinToString( + prefix = "mapOf(", + separator = ", ", + postfix = ")", + transform = { + val (key, value) = (it as Map.Entry) + val keyFormat = (keyType ?: key?.let { key::class.asTypeName() }).format() + val valueFormat = (valueType ?: value?.let { value::class.asTypeName() }).format() + + "$keyFormat to $valueFormat" + } + ) to elements.size * 2 return when (val nonNullable = copy(nullable = false)) { LONG, STRING -> singleFormat() - ARRAY -> arrayFormat { it::class.asTypeName() } - BYTE_ARRAY -> elements.format("byteArrayOf") { BYTE } - SHORT_ARRAY -> elements.format("shortArrayOf") { SHORT } - CHAR_ARRAY -> elements.format("charArrayOf") { CHAR } - INT_ARRAY -> elements.format("intArrayOf") { INT } - LONG_ARRAY -> elements.format("longArrayOf") { LONG } - FLOAT_ARRAY -> elements.format("floatArrayOf") { FLOAT } - DOUBLE_ARRAY -> elements.format("doubleArrayOf") { DOUBLE } - BOOLEAN_ARRAY -> elements.format("booleanArrayOf") { BOOLEAN } - LIST, GENERIC_LIST -> listFormat { it::class.asTypeName() } - SET, GENERIC_SET -> setFormat { it::class.asTypeName() } + ARRAY -> arrayFormat(null) + BYTE_ARRAY -> elements.format("byteArrayOf", BYTE) + SHORT_ARRAY -> elements.format("shortArrayOf", SHORT) + CHAR_ARRAY -> elements.format("charArrayOf", CHAR) + INT_ARRAY -> elements.format("intArrayOf", INT) + LONG_ARRAY -> elements.format("longArrayOf", LONG) + FLOAT_ARRAY -> elements.format("floatArrayOf", FLOAT) + DOUBLE_ARRAY -> elements.format("doubleArrayOf", DOUBLE) + BOOLEAN_ARRAY -> elements.format("booleanArrayOf", BOOLEAN) + LIST, GENERIC_LIST -> listFormat(null) + SET, GENERIC_SET -> setFormat(null) + MAP, GENERIC_MAP -> mapFormat(null, null) is ParameterizedTypeName -> when (nonNullable.rawType) { - ARRAY -> arrayFormat { nonNullable.typeArguments.first() } - LIST, GENERIC_LIST -> listFormat { nonNullable.typeArguments.first() } - SET, GENERIC_SET -> setFormat { nonNullable.typeArguments.first() } + ARRAY -> arrayFormat(nonNullable.typeArguments[0]) + LIST, GENERIC_LIST -> listFormat(nonNullable.typeArguments[0]) + SET, GENERIC_SET -> setFormat(nonNullable.typeArguments[0]) + MAP, GENERIC_MAP -> mapFormat(nonNullable.typeArguments[0], nonNullable.typeArguments[1]) else -> singleFormat() } @@ -204,6 +221,7 @@ data class BuildConfigKotlinGenerator( private val CONST_TYPES = setOf(STRING, BOOLEAN, BYTE, SHORT, INT, LONG, CHAR, FLOAT, DOUBLE) private val GENERIC_LIST = ClassName("", "List") private val GENERIC_SET = ClassName("", "Set") + private val GENERIC_MAP = ClassName("", "Map") private val FILE = File::class.asClassName() private val URI = URI::class.asClassName() } diff --git a/plugin/src/main/kotlin/org/gradle/kotlin/dsl/BuildConfigClassSpecDSL.kt b/plugin/src/main/kotlin/org/gradle/kotlin/dsl/BuildConfigClassSpecDSL.kt index 0a2a7b9..20fc0f4 100644 --- a/plugin/src/main/kotlin/org/gradle/kotlin/dsl/BuildConfigClassSpecDSL.kt +++ b/plugin/src/main/kotlin/org/gradle/kotlin/dsl/BuildConfigClassSpecDSL.kt @@ -87,6 +87,16 @@ inline fun BuildConfigClassSpec.buildConfigField( it.value(LinkedHashSet(value)) }) +@BuildConfigDsl +@JvmName("buildConfigFieldMap") +inline fun BuildConfigClassSpec.buildConfigField( + name: String, + value: Map, +) = buildConfigField(name, Action { + it.type(typeOf>()) + it.value(LinkedHashMap(value)) +}) + @BuildConfigDsl @JvmName("buildConfigFieldArray") inline fun BuildConfigClassSpec.buildConfigField( @@ -116,3 +126,13 @@ inline fun BuildConfigClassSpec.buildConfigField( it.type(typeOf>()) it.value(value.map(::LinkedHashSet)) }) + +@BuildConfigDsl +@JvmName("buildConfigFieldMap") +inline fun BuildConfigClassSpec.buildConfigField( + name: String, + value: Provider>, +) = buildConfigField(name, Action { + it.type(typeOf>()) + it.value(value.map(::LinkedHashMap)) +})