mirror of
https://github.com/apple/pkl.git
synced 2026-04-21 07:51:35 +02:00
Allow renaming Java/Kotlin classes/packages during code generation (#499)
Adds a `rename` field to the Java/Kotlin code generators that allows renaming packages and classes during codegen. * Add `--rename` flag to CLIs * Add `rename` property to Gradle API
This commit is contained in:
@@ -36,8 +36,23 @@ data class CliKotlinCodeGeneratorOptions(
|
||||
val generateSpringBootConfig: Boolean = false,
|
||||
|
||||
/** Whether to make generated classes implement [java.io.Serializable] */
|
||||
val implementSerializable: Boolean = false
|
||||
val implementSerializable: Boolean = false,
|
||||
|
||||
/**
|
||||
* A rename mapping for class names.
|
||||
*
|
||||
* When you need to have Kotlin class or package names different from the default names derived
|
||||
* from Pkl module names, you can define a rename mapping, where the key is a prefix of the
|
||||
* original Pkl module name, and the value is the desired replacement.
|
||||
*/
|
||||
val renames: Map<String, String> = emptyMap()
|
||||
) {
|
||||
fun toKotlinCodegenOptions(): KotlinCodegenOptions =
|
||||
KotlinCodegenOptions(indent, generateKdoc, generateSpringBootConfig, implementSerializable)
|
||||
KotlinCodegenOptions(
|
||||
indent,
|
||||
generateKdoc,
|
||||
generateSpringBootConfig,
|
||||
implementSerializable,
|
||||
renames
|
||||
)
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ import com.squareup.kotlinpoet.ParameterizedTypeName.Companion.parameterizedBy
|
||||
import java.io.StringWriter
|
||||
import java.net.URI
|
||||
import java.util.*
|
||||
import org.pkl.commons.NameMapper
|
||||
import org.pkl.core.*
|
||||
import org.pkl.core.util.CodeGeneratorUtils
|
||||
import org.pkl.core.util.IoUtils
|
||||
@@ -35,7 +36,15 @@ data class KotlinCodegenOptions(
|
||||
val generateSpringBootConfig: Boolean = false,
|
||||
|
||||
/** Whether to make generated classes implement [java.io.Serializable] */
|
||||
val implementSerializable: Boolean = false
|
||||
val implementSerializable: Boolean = false,
|
||||
|
||||
/**
|
||||
* A mapping from Pkl module name prefixes to their replacements.
|
||||
*
|
||||
* Can be used when the class or package name in the generated source code should be different
|
||||
* from the corresponding name derived from the Pkl module declaration .
|
||||
*/
|
||||
val renames: Map<String, String> = emptyMap(),
|
||||
)
|
||||
|
||||
class KotlinCodeGeneratorException(message: String) : RuntimeException(message)
|
||||
@@ -106,10 +115,17 @@ class KotlinCodeGenerator(
|
||||
.toString()
|
||||
}
|
||||
|
||||
val kotlinFileName: String
|
||||
private val kotlinFileName: String
|
||||
get() = buildString {
|
||||
val (packageName, className) = nameMapper.map(moduleSchema.moduleName)
|
||||
val dirPath = packageName.split('.').joinToString("/", transform = IoUtils::encodePath)
|
||||
val fileName = IoUtils.encodePath(className)
|
||||
append("kot")
|
||||
append("lin/${relativeOutputPathFor(moduleSchema.moduleName)}")
|
||||
append("lin/")
|
||||
if (dirPath.isNotEmpty()) {
|
||||
append("$dirPath/")
|
||||
}
|
||||
append("$fileName.kt")
|
||||
}
|
||||
|
||||
val kotlinFile: String
|
||||
@@ -169,9 +185,8 @@ class KotlinCodeGenerator(
|
||||
}
|
||||
|
||||
val moduleName = moduleSchema.moduleName
|
||||
val index = moduleName.lastIndexOf(".")
|
||||
val packageName = if (index == -1) "" else moduleName.substring(0, index)
|
||||
val moduleTypeName = moduleName.substring(index + 1).replaceFirstChar { it.titlecaseChar() }
|
||||
|
||||
val (packageName, moduleTypeName) = nameMapper.map(moduleName)
|
||||
|
||||
val fileSpec = FileSpec.builder(packageName, moduleTypeName).indent(options.indent)
|
||||
|
||||
@@ -195,17 +210,6 @@ class KotlinCodeGenerator(
|
||||
return fileSpec.build().toString()
|
||||
}
|
||||
|
||||
private fun relativeOutputPathFor(moduleName: String): String {
|
||||
val nameParts = moduleName.split(".").map(IoUtils::encodePath)
|
||||
val dirPath = nameParts.dropLast(1).joinToString("/")
|
||||
val fileName = nameParts.last().replaceFirstChar { it.titlecaseChar() }
|
||||
return if (dirPath.isEmpty()) {
|
||||
"$fileName.kt"
|
||||
} else {
|
||||
"$dirPath/$fileName.kt"
|
||||
}
|
||||
}
|
||||
|
||||
private fun generateObjectSpec(pClass: PClass): TypeSpec.Builder {
|
||||
val builder = TypeSpec.objectBuilder(pClass.toKotlinPoetName())
|
||||
val docComment = pClass.docComment
|
||||
@@ -636,9 +640,7 @@ class KotlinCodeGenerator(
|
||||
.endControlFlow()
|
||||
|
||||
private fun PClass.toKotlinPoetName(): ClassName {
|
||||
val index = moduleName.lastIndexOf(".")
|
||||
val packageName = if (index == -1) "" else moduleName.substring(0, index)
|
||||
val moduleTypeName = moduleName.substring(index + 1).replaceFirstChar { it.titlecaseChar() }
|
||||
val (packageName, moduleTypeName) = nameMapper.map(moduleName)
|
||||
return if (isModuleClass) {
|
||||
ClassName(packageName, moduleTypeName)
|
||||
} else {
|
||||
@@ -647,8 +649,7 @@ class KotlinCodeGenerator(
|
||||
}
|
||||
|
||||
private fun TypeAlias.toKotlinPoetName(): ClassName {
|
||||
val index = moduleName.lastIndexOf(".")
|
||||
val packageName = if (index == -1) "" else moduleName.substring(0, index)
|
||||
val (packageName, moduleTypeName) = nameMapper.map(moduleName)
|
||||
|
||||
return when {
|
||||
aliasedType is PType.Alias -> {
|
||||
@@ -663,7 +664,6 @@ class KotlinCodeGenerator(
|
||||
)
|
||||
}
|
||||
// Kotlin type generated for [this] is a nested enum class
|
||||
val moduleTypeName = moduleName.substring(index + 1).replaceFirstChar { it.titlecaseChar() }
|
||||
ClassName(packageName, moduleTypeName, simpleName)
|
||||
}
|
||||
else -> {
|
||||
@@ -786,4 +786,6 @@ class KotlinCodeGenerator(
|
||||
|
||||
private fun List<PType>.toKotlinPoet(): Array<TypeName> =
|
||||
map { it.toKotlinPoetName() }.toTypedArray()
|
||||
|
||||
private val nameMapper = NameMapper(options.renames)
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
|
||||
package org.pkl.codegen.kotlin
|
||||
|
||||
import com.github.ajalt.clikt.parameters.options.associate
|
||||
import com.github.ajalt.clikt.parameters.options.default
|
||||
import com.github.ajalt.clikt.parameters.options.flag
|
||||
import com.github.ajalt.clikt.parameters.options.option
|
||||
@@ -81,6 +82,21 @@ class PklKotlinCodegenCommand :
|
||||
)
|
||||
.flag()
|
||||
|
||||
private val renames: Map<String, String> by
|
||||
option(
|
||||
names = arrayOf("--rename"),
|
||||
metavar = "<old_name=new_name>",
|
||||
help =
|
||||
"""
|
||||
Replace a prefix in the names of the generated Kotlin classes (repeatable).
|
||||
By default, the names of generated classes are derived from the Pkl module names.
|
||||
With this option, you can override the modify the default names, renaming entire
|
||||
classes or just their packages.
|
||||
"""
|
||||
.trimIndent()
|
||||
)
|
||||
.associate()
|
||||
|
||||
override fun run() {
|
||||
val options =
|
||||
CliKotlinCodeGeneratorOptions(
|
||||
@@ -89,7 +105,8 @@ class PklKotlinCodegenCommand :
|
||||
indent = indent,
|
||||
generateKdoc = generateKdoc,
|
||||
generateSpringBootConfig = generateSpringboot,
|
||||
implementSerializable = implementSerializable
|
||||
implementSerializable = implementSerializable,
|
||||
renames = renames
|
||||
)
|
||||
CliKotlinCodeGenerator(options).run()
|
||||
}
|
||||
|
||||
@@ -152,6 +152,119 @@ class CliKotlinCodeGeneratorTest {
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `custom package names`(@TempDir tempDir: Path) {
|
||||
val module1 =
|
||||
PklModule(
|
||||
"org.foo.Module1",
|
||||
"""
|
||||
module org.foo.Module1
|
||||
|
||||
class Person {
|
||||
name: String
|
||||
}
|
||||
"""
|
||||
)
|
||||
|
||||
val module2 =
|
||||
PklModule(
|
||||
"org.bar.Module2",
|
||||
"""
|
||||
module org.bar.Module2
|
||||
|
||||
import "../../org/foo/Module1.pkl"
|
||||
|
||||
class Group {
|
||||
owner: Module1.Person
|
||||
name: String
|
||||
}
|
||||
"""
|
||||
)
|
||||
|
||||
val module3 =
|
||||
PklModule(
|
||||
"org.baz.Module3",
|
||||
"""
|
||||
module org.baz.Module3
|
||||
|
||||
import "../../org/bar/Module2.pkl"
|
||||
|
||||
class Supergroup {
|
||||
owner: Module2.Group
|
||||
}
|
||||
"""
|
||||
)
|
||||
|
||||
val module1PklFile = module1.writeToDisk(tempDir.resolve("org/foo/Module1.pkl"))
|
||||
val module2PklFile = module2.writeToDisk(tempDir.resolve("org/bar/Module2.pkl"))
|
||||
val module3PklFile = module3.writeToDisk(tempDir.resolve("org/baz/Module3.pkl"))
|
||||
val outputDir = tempDir.resolve("output")
|
||||
|
||||
val generator =
|
||||
CliKotlinCodeGenerator(
|
||||
CliKotlinCodeGeneratorOptions(
|
||||
CliBaseOptions(listOf(module1PklFile, module2PklFile, module3PklFile).map { it.toUri() }),
|
||||
outputDir,
|
||||
renames = mapOf("org.foo" to "com.foo.x", "org.baz" to "com.baz.a.b")
|
||||
)
|
||||
)
|
||||
|
||||
generator.run()
|
||||
|
||||
val module1KotlinFile = outputDir.resolve("kotlin/com/foo/x/Module1.kt")
|
||||
module1KotlinFile.readString().let {
|
||||
assertContains("package com.foo.x", it)
|
||||
|
||||
assertContains("object Module1 {", it)
|
||||
|
||||
assertContains(
|
||||
"""
|
||||
| data class Person(
|
||||
| val name: String
|
||||
| )
|
||||
""",
|
||||
it
|
||||
)
|
||||
}
|
||||
|
||||
val module2KotlinFile = outputDir.resolve("kotlin/org/bar/Module2.kt")
|
||||
module2KotlinFile.readString().let {
|
||||
assertContains("package org.bar", it)
|
||||
|
||||
assertContains("import com.foo.x.Module1", it)
|
||||
|
||||
assertContains("object Module2 {", it)
|
||||
|
||||
assertContains(
|
||||
"""
|
||||
| data class Group(
|
||||
| val owner: Module1.Person,
|
||||
| val name: String
|
||||
| )
|
||||
""",
|
||||
it
|
||||
)
|
||||
}
|
||||
|
||||
val module3KotlinFile = outputDir.resolve("kotlin/com/baz/a/b/Module3.kt")
|
||||
module3KotlinFile.readString().let {
|
||||
assertContains("package com.baz.a.b", it)
|
||||
|
||||
assertContains("import org.bar.Module2", it)
|
||||
|
||||
assertContains("object Module3 {", it)
|
||||
|
||||
assertContains(
|
||||
"""
|
||||
| data class Supergroup(
|
||||
| val owner: Module2.Group
|
||||
| )
|
||||
""",
|
||||
it
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun assertContains(part: String, code: String) {
|
||||
val trimmedPart = part.trim().trimMargin()
|
||||
if (!code.contains(trimmedPart)) {
|
||||
|
||||
@@ -56,6 +56,7 @@ object InMemoryKotlinCompiler {
|
||||
|
||||
val (importLines, remainder) =
|
||||
sourceFiles.entries
|
||||
.filter { (filename, _) -> filename.endsWith(".kt") }
|
||||
.flatMap { (_, text) -> text.lines() }
|
||||
.partition { it.startsWith("import") }
|
||||
val importBlock = importLines.sorted().distinct()
|
||||
|
||||
@@ -32,6 +32,8 @@ import org.pkl.core.util.IoUtils
|
||||
|
||||
class KotlinCodeGeneratorTest {
|
||||
companion object {
|
||||
const val MAPPER_PREFIX = "resources/META-INF/org/pkl/config/java/mapper/classes"
|
||||
|
||||
// according to:
|
||||
// https://github.com/JetBrains/kotlin/blob/master/core/descriptors/
|
||||
// src/org/jetbrains/kotlin/renderer/KeywordStringsGenerated.java
|
||||
@@ -153,9 +155,13 @@ class KotlinCodeGeneratorTest {
|
||||
private fun compileKotlinCode(kotlinCode: String): Map<String, KClass<*>> =
|
||||
InMemoryKotlinCompiler.compile(mapOf("my/Mod.kt" to kotlinCode))
|
||||
|
||||
private fun assertCompilesSuccessfully(sourceText: String) = compileKotlinCode(sourceText)
|
||||
private fun assertCompilesSuccessfully(sourceText: String) {
|
||||
assertThatCode { compileKotlinCode(sourceText) }.doesNotThrowAnyException()
|
||||
}
|
||||
}
|
||||
|
||||
@TempDir lateinit var tempDir: Path
|
||||
|
||||
@Test
|
||||
fun testEquals() {
|
||||
val ctor = simpleClass.constructors.first()
|
||||
@@ -1206,7 +1212,7 @@ class KotlinCodeGeneratorTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `import module`(@TempDir tempDir: Path) {
|
||||
fun `import module`() {
|
||||
val library =
|
||||
PklModule(
|
||||
"library",
|
||||
@@ -1235,7 +1241,7 @@ class KotlinCodeGeneratorTest {
|
||||
.trimIndent()
|
||||
)
|
||||
|
||||
val kotlinSourceFiles = generateKotlinFiles(tempDir, library, client)
|
||||
val kotlinSourceFiles = generateFiles(library, client)
|
||||
val kotlinClientCode =
|
||||
kotlinSourceFiles.entries.find { (fileName, _) -> fileName.endsWith("Client.kt") }!!.value
|
||||
|
||||
@@ -1253,7 +1259,7 @@ class KotlinCodeGeneratorTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `extend module`(@TempDir tempDir: Path) {
|
||||
fun `extend module`() {
|
||||
val base =
|
||||
PklModule(
|
||||
"base",
|
||||
@@ -1282,7 +1288,7 @@ class KotlinCodeGeneratorTest {
|
||||
.trimIndent()
|
||||
)
|
||||
|
||||
val kotlinSourceFiles = generateKotlinFiles(tempDir, base, derived)
|
||||
val kotlinSourceFiles = generateFiles(base, derived)
|
||||
val kotlinDerivedCode =
|
||||
kotlinSourceFiles.entries.find { (filename, _) -> filename.endsWith("Derived.kt") }!!.value
|
||||
|
||||
@@ -1317,7 +1323,7 @@ class KotlinCodeGeneratorTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `extend module that only contains type aliases`(@TempDir tempDir: Path) {
|
||||
fun `extend module that only contains type aliases`() {
|
||||
val moduleOne =
|
||||
PklModule(
|
||||
"base",
|
||||
@@ -1342,7 +1348,7 @@ class KotlinCodeGeneratorTest {
|
||||
.trimIndent()
|
||||
)
|
||||
|
||||
val kotlinSourceFiles = generateKotlinFiles(tempDir, moduleOne, moduleTwo)
|
||||
val kotlinSourceFiles = generateFiles(moduleOne, moduleTwo)
|
||||
val kotlinDerivedCode =
|
||||
kotlinSourceFiles.entries.find { (filename, _) -> filename.endsWith("Derived.kt") }!!.value
|
||||
|
||||
@@ -1359,7 +1365,7 @@ class KotlinCodeGeneratorTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `generated properties files`(@TempDir tempDir: Path) {
|
||||
fun `generated properties files`() {
|
||||
val pklModule =
|
||||
PklModule(
|
||||
"Mod.pkl",
|
||||
@@ -1380,17 +1386,17 @@ class KotlinCodeGeneratorTest {
|
||||
"""
|
||||
.trimIndent()
|
||||
)
|
||||
val generated = generateFiles(tempDir, pklModule)
|
||||
val generated = generateFiles(pklModule)
|
||||
val expectedPropertyFile =
|
||||
"resources/META-INF/org/pkl/config/java/mapper/classes/org.pkl.Mod.properties"
|
||||
assertThat(generated).containsKey(expectedPropertyFile)
|
||||
val propertyFileContents = generated[expectedPropertyFile]!!
|
||||
assertThat(propertyFileContents)
|
||||
.contains("org.pkl.config.java.mapper.org.pkl.Mod\\#ModuleClass=org.pkl.Mod")
|
||||
assertThat(propertyFileContents)
|
||||
.contains("org.pkl.config.java.mapper.org.pkl.Mod\\#Foo=org.pkl.Mod\$Foo")
|
||||
assertThat(propertyFileContents)
|
||||
.contains("org.pkl.config.java.mapper.org.pkl.Mod\\#Bar=org.pkl.Mod\$Bar")
|
||||
.contains(
|
||||
"org.pkl.config.java.mapper.org.pkl.Mod\\#ModuleClass=org.pkl.Mod",
|
||||
"org.pkl.config.java.mapper.org.pkl.Mod\\#Foo=org.pkl.Mod\$Foo",
|
||||
"org.pkl.config.java.mapper.org.pkl.Mod\\#Bar=org.pkl.Mod\$Bar"
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -1502,45 +1508,253 @@ class KotlinCodeGeneratorTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `encoded file paths`(@TempDir path: Path) {
|
||||
fun `encoded file paths`() {
|
||||
val kotlinCode =
|
||||
generateKotlinFiles(
|
||||
path,
|
||||
generateFiles(
|
||||
PklModule(
|
||||
"FooBar.pkl",
|
||||
"""
|
||||
module `Foo*Bar`
|
||||
|
||||
someProp: String
|
||||
"""
|
||||
module `Foo*Bar`
|
||||
|
||||
someProp: String
|
||||
"""
|
||||
.trimIndent()
|
||||
)
|
||||
)
|
||||
assertThat(kotlinCode).containsKey("kotlin/Foo(2a)Bar.kt")
|
||||
}
|
||||
|
||||
private fun generateFiles(tempDir: Path, vararg pklModules: PklModule): Map<String, String> {
|
||||
val pklFiles = pklModules.map { it.writeToDisk(tempDir.resolve("pkl/${it.name}.pkl")) }
|
||||
val evaluator = Evaluator.preconfigured()
|
||||
return pklFiles.fold(mapOf()) { acc, pklFile ->
|
||||
val pklSchema = evaluator.evaluateSchema(ModuleSource.path(pklFile))
|
||||
acc + KotlinCodeGenerator(pklSchema, KotlinCodegenOptions()).output
|
||||
}
|
||||
@Test
|
||||
fun `override names in a standalone module`() {
|
||||
val files =
|
||||
KotlinCodegenOptions(
|
||||
renames = mapOf("a.b.c" to "x.y.z", "d.e.f.AnotherModule" to "u.v.w.RenamedModule")
|
||||
)
|
||||
.generateFiles(
|
||||
"MyModule.pkl" to
|
||||
"""
|
||||
module a.b.c.MyModule
|
||||
|
||||
foo: String = "abc"
|
||||
"""
|
||||
.trimIndent(),
|
||||
"AnotherModule.pkl" to
|
||||
"""
|
||||
module d.e.f.AnotherModule
|
||||
|
||||
bar: Int = 123
|
||||
"""
|
||||
.trimIndent()
|
||||
)
|
||||
.toMutableMap()
|
||||
|
||||
files.validateContents(
|
||||
"kotlin/x/y/z/MyModule.kt" to listOf("package x.y.z", "data class MyModule("),
|
||||
"$MAPPER_PREFIX/a.b.c.MyModule.properties" to
|
||||
listOf("org.pkl.config.java.mapper.a.b.c.MyModule\\#ModuleClass=x.y.z.MyModule"),
|
||||
// ---
|
||||
"kotlin/u/v/w/RenamedModule.kt" to listOf("package u.v.w", "data class RenamedModule("),
|
||||
"$MAPPER_PREFIX/d.e.f.AnotherModule.properties" to
|
||||
listOf("org.pkl.config.java.mapper.d.e.f.AnotherModule\\#ModuleClass=u.v.w.RenamedModule"),
|
||||
)
|
||||
}
|
||||
|
||||
private fun generateKotlinFiles(
|
||||
tempDir: Path,
|
||||
@Test
|
||||
fun `override names based on the longest prefix`() {
|
||||
val files =
|
||||
KotlinCodegenOptions(
|
||||
renames = mapOf("com.foo.bar." to "x.", "com.foo." to "y.", "com." to "z.", "" to "w.")
|
||||
)
|
||||
.generateFiles(
|
||||
"com/foo/bar/Module1" to
|
||||
"""
|
||||
module com.foo.bar.Module1
|
||||
|
||||
bar: String
|
||||
"""
|
||||
.trimIndent(),
|
||||
"com/Module2" to
|
||||
"""
|
||||
module com.Module2
|
||||
|
||||
com: String
|
||||
"""
|
||||
.trimIndent(),
|
||||
"org/baz/Module3" to
|
||||
"""
|
||||
module org.baz.Module3
|
||||
|
||||
baz: String
|
||||
"""
|
||||
.trimIndent()
|
||||
)
|
||||
.toMutableMap()
|
||||
|
||||
files.validateContents(
|
||||
"kotlin/x/Module1.kt" to listOf("package x", "data class Module1("),
|
||||
"$MAPPER_PREFIX/com.foo.bar.Module1.properties" to
|
||||
listOf("org.pkl.config.java.mapper.com.foo.bar.Module1\\#ModuleClass=x.Module1"),
|
||||
// ---
|
||||
"kotlin/z/Module2.kt" to listOf("package z", "data class Module2("),
|
||||
"$MAPPER_PREFIX/com.Module2.properties" to
|
||||
listOf("org.pkl.config.java.mapper.com.Module2\\#ModuleClass=z.Module2"),
|
||||
// ---
|
||||
"kotlin/w/org/baz/Module3.kt" to listOf("package w.org.baz", "data class Module3("),
|
||||
"$MAPPER_PREFIX/org.baz.Module3.properties" to
|
||||
listOf("org.pkl.config.java.mapper.org.baz.Module3\\#ModuleClass=w.org.baz.Module3")
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `override names in multiple modules using each other`() {
|
||||
val files =
|
||||
KotlinCodegenOptions(
|
||||
renames =
|
||||
mapOf(
|
||||
"org.foo" to "com.foo.x",
|
||||
"org.bar.Module2" to "org.bar.RenamedModule",
|
||||
"org.baz" to "com.baz.a.b"
|
||||
)
|
||||
)
|
||||
.generateFiles(
|
||||
"org/foo/Module1" to
|
||||
"""
|
||||
module org.foo.Module1
|
||||
|
||||
class Person {
|
||||
name: String
|
||||
}
|
||||
"""
|
||||
.trimIndent(),
|
||||
"org/bar/Module2" to
|
||||
"""
|
||||
module org.bar.Module2
|
||||
|
||||
import "../../org/foo/Module1.pkl"
|
||||
|
||||
class Group {
|
||||
owner: Module1.Person
|
||||
name: String
|
||||
}
|
||||
"""
|
||||
.trimIndent(),
|
||||
"org/baz/Module3" to
|
||||
"""
|
||||
module org.baz.Module3
|
||||
|
||||
import "../../org/bar/Module2.pkl"
|
||||
|
||||
class Supergroup {
|
||||
owner: Module2.Group
|
||||
}
|
||||
"""
|
||||
.trimIndent()
|
||||
)
|
||||
|
||||
files.validateContents(
|
||||
"kotlin/com/foo/x/Module1.kt" to
|
||||
listOf("package com.foo.x", "object Module1 {", "data class Person("),
|
||||
"$MAPPER_PREFIX/org.foo.Module1.properties" to
|
||||
listOf(
|
||||
"org.pkl.config.java.mapper.org.foo.Module1\\#ModuleClass=com.foo.x.Module1",
|
||||
"org.pkl.config.java.mapper.org.foo.Module1\\#Person=com.foo.x.Module1${'$'}Person",
|
||||
),
|
||||
// ---
|
||||
"kotlin/org/bar/RenamedModule.kt" to
|
||||
listOf(
|
||||
"package org.bar",
|
||||
"import com.foo.x.Module1",
|
||||
"object RenamedModule {",
|
||||
"val owner: Module1.Person"
|
||||
),
|
||||
"$MAPPER_PREFIX/org.bar.Module2.properties" to
|
||||
listOf(
|
||||
"org.pkl.config.java.mapper.org.bar.Module2\\#ModuleClass=org.bar.RenamedModule",
|
||||
"org.pkl.config.java.mapper.org.bar.Module2\\#Group=org.bar.RenamedModule${'$'}Group",
|
||||
),
|
||||
// ---
|
||||
"kotlin/com/baz/a/b/Module3.kt" to
|
||||
listOf(
|
||||
"package com.baz.a.b",
|
||||
"import org.bar.RenamedModule",
|
||||
"object Module3 {",
|
||||
"val owner: RenamedModule.Group"
|
||||
),
|
||||
"$MAPPER_PREFIX/org.baz.Module3.properties" to
|
||||
listOf(
|
||||
"org.pkl.config.java.mapper.org.baz.Module3\\#ModuleClass=com.baz.a.b.Module3",
|
||||
"org.pkl.config.java.mapper.org.baz.Module3\\#Supergroup=com.baz.a.b.Module3${'$'}Supergroup",
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `do not capitalize names of renamed classes`() {
|
||||
val files =
|
||||
KotlinCodegenOptions(
|
||||
renames = mapOf("a.b.c.MyModule" to "x.y.z.renamed_module", "d.e.f." to "u.v.w.")
|
||||
)
|
||||
.generateFiles(
|
||||
"MyModule.pkl" to
|
||||
"""
|
||||
module a.b.c.MyModule
|
||||
|
||||
foo: String = "abc"
|
||||
"""
|
||||
.trimIndent(),
|
||||
"lower_module.pkl" to
|
||||
"""
|
||||
module d.e.f.lower_module
|
||||
|
||||
bar: Int = 123
|
||||
"""
|
||||
.trimIndent()
|
||||
)
|
||||
|
||||
files.validateContents(
|
||||
"kotlin/x/y/z/renamed_module.kt" to listOf("package x.y.z", "data class renamed_module("),
|
||||
"$MAPPER_PREFIX/a.b.c.MyModule.properties" to
|
||||
listOf("org.pkl.config.java.mapper.a.b.c.MyModule\\#ModuleClass=x.y.z.renamed_module"),
|
||||
// ---
|
||||
"kotlin/u/v/w/Lower_module.kt" to listOf("package u.v.w", "data class Lower_module("),
|
||||
"$MAPPER_PREFIX/d.e.f.lower_module.properties" to
|
||||
listOf("org.pkl.config.java.mapper.d.e.f.lower_module\\#ModuleClass=u.v.w.Lower_module"),
|
||||
)
|
||||
}
|
||||
|
||||
private fun Map<String, String>.validateContents(
|
||||
vararg assertions: kotlin.Pair<String, List<String>>
|
||||
) {
|
||||
val files = toMutableMap()
|
||||
|
||||
for ((fileName, lines) in assertions) {
|
||||
assertThat(files).containsKey(fileName)
|
||||
assertThat(files.remove(fileName)).describedAs("Contents of $fileName").contains(lines)
|
||||
}
|
||||
|
||||
assertThat(files).isEmpty()
|
||||
}
|
||||
|
||||
private fun KotlinCodegenOptions.generateFiles(
|
||||
vararg pklModules: PklModule
|
||||
): Map<String, String> {
|
||||
val pklFiles = pklModules.map { it.writeToDisk(tempDir.resolve("pkl/${it.name}.pkl")) }
|
||||
val evaluator = Evaluator.preconfigured()
|
||||
return pklFiles.fold(mapOf()) { acc, pklFile ->
|
||||
val pklSchema = evaluator.evaluateSchema(ModuleSource.path(pklFile))
|
||||
val generator = KotlinCodeGenerator(pklSchema, KotlinCodegenOptions())
|
||||
acc + arrayOf(generator.kotlinFileName to generator.kotlinFile)
|
||||
val generator = KotlinCodeGenerator(pklSchema, this)
|
||||
acc + generator.output
|
||||
}
|
||||
}
|
||||
|
||||
private fun KotlinCodegenOptions.generateFiles(
|
||||
vararg pklModules: kotlin.Pair<String, String>
|
||||
): Map<String, String> =
|
||||
generateFiles(*pklModules.map { (name, text) -> PklModule(name, text) }.toTypedArray())
|
||||
|
||||
private fun generateFiles(vararg pklModules: PklModule): Map<String, String> =
|
||||
KotlinCodegenOptions().generateFiles(*pklModules)
|
||||
|
||||
private fun instantiateOtherAndPropertyTypes(): kotlin.Pair<Any, Any> {
|
||||
val otherCtor = propertyTypesClasses.getValue("Other").constructors.first()
|
||||
val other = otherCtor.call("pigeon")
|
||||
|
||||
Reference in New Issue
Block a user