From 074228807e33b1fdf63d4a1d41b728b02151521d Mon Sep 17 00:00:00 2001 From: Fabrice Lecomte Date: Wed, 5 Apr 2023 23:15:17 +0200 Subject: [PATCH] Improve generate() --- .../functionGenerator/FunctionGenerator.kt | 95 +++++++++++------- .../kotlin/fr/postgresjson/TestAbstract.kt | 4 +- .../FunctionGeneratorTest.kt | 99 ++++++++++++++++++- 3 files changed, 156 insertions(+), 42 deletions(-) diff --git a/src/main/kotlin/fr/postgresjson/functionGenerator/FunctionGenerator.kt b/src/main/kotlin/fr/postgresjson/functionGenerator/FunctionGenerator.kt index fc44f55..770f0d3 100644 --- a/src/main/kotlin/fr/postgresjson/functionGenerator/FunctionGenerator.kt +++ b/src/main/kotlin/fr/postgresjson/functionGenerator/FunctionGenerator.kt @@ -32,9 +32,10 @@ class FunctionGenerator(private val functionsDirectories: List) { } } - base+default + base + default } } + private fun List.toMapOf(): String { return filter { it.direction == IN || it.direction == INOUT } .joinToString(", ", prefix = "mapOf(", postfix = ")") { """"${it.kotlinName}" to ${it.kotlinName}""" } @@ -49,6 +50,7 @@ class FunctionGenerator(private val functionsDirectories: List) { "character" -> "String" "char" -> "String" "int" -> "Int" + "float" -> "Float" "boolean" -> "Boolean" "json" -> "S" "jsonb" -> "S" @@ -69,45 +71,68 @@ class FunctionGenerator(private val functionsDirectories: List) { return name.toCamelCase().trimStart('_') } + private val functions: List + get() = functionsDirectories + .flatMap { it.searchSqlFiles() } + .filterIsInstance() + fun generate(outputDirectory: URI) { File(outputDirectory.path).apply { logger.debug("Create Directory: $absolutePath") mkdirs() } - this.functionsDirectories - .flatMap { it.searchSqlFiles() } - .filterIsInstance() - .map { it.run { - val args = parameters.toKotlinArgs() - - File("${outputDirectory.path}${kotlinName}.kt").apply { - logger.debug("Create kotlin file: $absolutePath") - val hasGenerics: Boolean = parameters.filter { it.direction != OUT }.any { it.kotlinType == "S" } - val genericsType = if (hasGenerics) ", S: Serializable" else "" - - val hasReturn = parameters.any { it.direction != IN } || (it.returns != "" && it.returns != "void") - val returnTypeGenerics = if (hasReturn) "reified E: EntityI" else "" - val returnType = if (hasReturn) ": List" else "" - val returnWord = if (hasReturn) "return " else "" - val select = if (hasReturn) "select" else "exec" - val function = if (hasGenerics || hasReturn) """inline fun <$returnTypeGenerics$genericsType>""" else "fun" - - val importEntityI = if (hasReturn) "import fr.postgresjson.entity.EntityI\n" else "" - val importSerializable = if (hasGenerics) "import fr.postgresjson.entity.Serializable\n" else "" - val importSelect = if (hasReturn) "import fr.postgresjson.connexion.select\n" else "" - - writeText(""" - |package fr.postgresjson.functionGenerator.generated - | - |import fr.postgresjson.connexion.Requester - |$importSelect$importSerializable$importEntityI - |$function Requester.$kotlinName($args)$returnType { - | ${returnWord}getFunction("${it.name}") - | .$select(${parameters.toMapOf()}) - |} - """.trimMargin()) - }} + functions + .map { function -> + File("${outputDirectory.path}${function.kotlinName}.kt").apply { + writeText(generate(function)) + } } } -} \ No newline at end of file + + fun generate(functionName: String): String { + return functions + .first { it.name == functionName } + .let { generate(it) } + } + + fun generate(function: Function): String = function.run { + val args = parameters.toKotlinArgs() + + val hasInputArgs: Boolean = parameters.filter { it.direction != OUT }.any { it.kotlinType == "S" } + val hasReturn: Boolean = parameters.any { it.direction != IN } || (returns != "" && returns != "void") + + val generics = mutableListOf() + if (hasReturn) generics.add("reified E: Any?") + if (hasInputArgs) generics.add("S: Serializable") + + val functionDecl = if (generics.isNotEmpty()) "inline fun <${generics.joinToString(", ")}>" else "fun" + + val importSerializable = if (hasInputArgs) "import fr.postgresjson.entity.Serializable\n" else "" + + if (hasReturn) { + return """ + |package fr.postgresjson.functionGenerator.generated + | + |import com.fasterxml.jackson.core.type.TypeReference + |import fr.postgresjson.connexion.Requester + |$importSerializable + |$functionDecl Requester.$kotlinName($args): E { + | return getFunction("$name") + | .selectAny(object : TypeReference() {}, ${parameters.toMapOf()}) + |} + """.trimMargin() + } else { + return """ + |package fr.postgresjson.functionGenerator.generated + | + |import fr.postgresjson.connexion.Requester + |$importSerializable + |$functionDecl Requester.$kotlinName($args): Unit { + | getFunction("$name") + | .exec(${parameters.toMapOf()}) + |} + """.trimMargin() + } + } +} diff --git a/src/test/kotlin/fr/postgresjson/TestAbstract.kt b/src/test/kotlin/fr/postgresjson/TestAbstract.kt index 433ed07..2589c0b 100644 --- a/src/test/kotlin/fr/postgresjson/TestAbstract.kt +++ b/src/test/kotlin/fr/postgresjson/TestAbstract.kt @@ -8,8 +8,8 @@ import org.junit.jupiter.api.TestInstance.Lifecycle.PER_CLASS import java.io.File @TestInstance(PER_CLASS) -abstract class TestAbstract { - protected val connection = Connection(database = "json_test", username = "test", password = "test", port = 5555) +open class TestAbstract { + protected val connection = Connection(database = "json_test", username = "test", password = "test", port = 35555) @BeforeEach fun beforeAll() { diff --git a/src/test/kotlin/fr/postgresjson/functionGenerator/FunctionGeneratorTest.kt b/src/test/kotlin/fr/postgresjson/functionGenerator/FunctionGeneratorTest.kt index 00cf5b7..6777e48 100644 --- a/src/test/kotlin/fr/postgresjson/functionGenerator/FunctionGeneratorTest.kt +++ b/src/test/kotlin/fr/postgresjson/functionGenerator/FunctionGeneratorTest.kt @@ -1,14 +1,103 @@ package fr.postgresjson.functionGenerator -import java.net.URI +import fr.postgresjson.definition.Function +import kotlin.test.assertEquals import org.junit.jupiter.api.Test class FunctionGeneratorTest { + private val functionDirectory = this::class.java.getResource("/sql/function/Test")!!.toURI() + private val generator = FunctionGenerator(functionDirectory) @Test - fun generate() { - val functionDirectory = this::class.java.getResource("/sql/function/Test")!!.toURI() - FunctionGenerator(functionDirectory) - .generate(URI( "./src/test/kotlin/fr/postgresjson/functionGenerator/generated/")) + fun `generate function`() { + val functionSql = """ + |create or replace function test_function_object (inout resource json) + |language plpgsql + |as + |$$ + |begin + | resource = json_build_object('id', '1e5f5d41-6d14-4007-897b-0ed2616bec96', 'name', 'changedName'); + |end; + |$$ + """.trimMargin() + + val expectedGenerated = """ + |package fr.postgresjson.functionGenerator.generated + | + |import com.fasterxml.jackson.core.type.TypeReference + |import fr.postgresjson.connexion.Requester + |import fr.postgresjson.entity.Serializable + | + |inline fun Requester.testFunctionObject(resource: S): E { + | return getFunction("test_function_object") + | .selectAny(object : TypeReference() {}, mapOf("resource" to resource)) + |} + """.trimMargin() + + val generated: String = generator.generate(Function(functionSql)) + + assertEquals(expectedGenerated, generated) + } + + @Test + fun `generate function with return void`() { + val functionSql = """ + |create or replace function test_function_void (name text default 'plop') returns void + |language plpgsql + |as + |$$ + |begin + | perform 1; + |end; + |$$; + """.trimMargin() + + val expectedGenerated = """ + |package fr.postgresjson.functionGenerator.generated + | + |import fr.postgresjson.connexion.Requester + | + |fun Requester.testFunctionVoid(name: String = "plop"): Unit { + | getFunction("test_function_void") + | .exec(mapOf("name" to name)) + |} + """.trimMargin() + + val generated: String = generator.generate(Function(functionSql)) + + assertEquals(expectedGenerated, generated) + } + + @Test + fun `generate function with multiple args and defaults`() { + val functionSql = """ + |create or replace function test_function_multiple (name text default 'plop', in hi text default 'hello', out result json) + |language plpgsql + |as + |$$ + |begin + | result = json_build_array( + | json_build_object('id', '457daad5-4f1b-4eb7-80ec-6882adb8cc7d', 'name', name), + | json_build_object('id', '8d20abb0-7f77-4b6c-9991-44acd3c88faa', 'name', hi) + | ); + |end; + |$$ + """.trimMargin() + + val expectedGenerated = """ + |package fr.postgresjson.functionGenerator.generated + | + |import com.fasterxml.jackson.core.type.TypeReference + |import fr.postgresjson.connexion.Requester + | + |inline fun Requester.testFunctionMultiple(name: String = "plop", hi: String = "hello"): E { + | return getFunction("test_function_multiple") + | .selectAny(object : TypeReference() {}, mapOf("name" to name, "hi" to hi)) + |} + """.trimMargin() + + val generated: String = generator.generate(Function(functionSql)) + + assertEquals(expectedGenerated, generated) } } \ No newline at end of file