Initial commit
This commit is contained in:
commit
865694be30
18
.editorconfig
Normal file
18
.editorconfig
Normal file
@ -0,0 +1,18 @@
|
||||
# editorconfig.org
|
||||
root = true
|
||||
|
||||
[*]
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
end_of_line = lf
|
||||
charset = utf-8
|
||||
trim_trailing_whitespace = true
|
||||
insert_final_newline = true
|
||||
|
||||
[*.{kt, kts}]
|
||||
indent_size = 4
|
||||
insert_final_newline = true
|
||||
continuation_indent_size=4
|
||||
max_line_length = 120
|
||||
disabled_rules = no-wildcard-imports
|
||||
kotlin_imports_layout = idea
|
||||
5
.gitignore
vendored
Normal file
5
.gitignore
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
target/
|
||||
.idea/
|
||||
*.iml
|
||||
*.ipr
|
||||
*.iws
|
||||
13
Makefile
Normal file
13
Makefile
Normal file
@ -0,0 +1,13 @@
|
||||
version = 1.0-SNAPSHOT
|
||||
jar = target/ktormgenerator-$(version).jar
|
||||
native-output = target/ktormgenerator-$(version)-linux-static
|
||||
|
||||
.PHONY = all
|
||||
|
||||
all: $(native-output)
|
||||
|
||||
$(native-output): $(jar)
|
||||
native-image --static --no-fallback --no-server -jar $< $@
|
||||
|
||||
$(jar):
|
||||
mvn package
|
||||
178
pom.xml
Normal file
178
pom.xml
Normal file
@ -0,0 +1,178 @@
|
||||
<project>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>be.vandewalleh</groupId>
|
||||
<artifactId>ktormgenerator</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
<properties>
|
||||
<java.version>11</java.version>
|
||||
<kotlin.version>1.4.10</kotlin.version>
|
||||
<kotlin.code.style>official</kotlin.code.style>
|
||||
<maven.compiler.target>${java.version}</maven.compiler.target>
|
||||
<maven.compiler.source>${java.version}</maven.compiler.source>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<main.class>be.vandewalleh.KtormgeneratorKt</main.class>
|
||||
<!-- versions -->
|
||||
<assertj.version>3.17.2</assertj.version>
|
||||
<junit.version>5.7.0</junit.version>
|
||||
<clikt.version>3.0.1</clikt.version>
|
||||
</properties>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.jetbrains.kotlin</groupId>
|
||||
<artifactId>kotlin-stdlib-jdk8</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.github.ajalt.clikt</groupId>
|
||||
<artifactId>clikt-jvm</artifactId>
|
||||
<version>${clikt.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.assertj</groupId>
|
||||
<artifactId>assertj-core</artifactId>
|
||||
<version>${assertj.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.junit.jupiter</groupId>
|
||||
<artifactId>junit-jupiter</artifactId>
|
||||
<version>${junit.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.junit.jupiter</groupId>
|
||||
<artifactId>junit-jupiter-params</artifactId>
|
||||
<version>${junit.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<dependencyManagement>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.jetbrains.kotlin</groupId>
|
||||
<artifactId>kotlin-bom</artifactId>
|
||||
<version>${kotlin.version}</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
<build>
|
||||
<sourceDirectory>${project.basedir}/src/main/kotlin</sourceDirectory>
|
||||
<testSourceDirectory>${project.basedir}/src/test/kotlin</testSourceDirectory>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>3.8.1</version>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-dependency-plugin</artifactId>
|
||||
<version>3.1.2</version>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<version>3.0.0-M4</version>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.apache.maven.surefire</groupId>
|
||||
<artifactId>surefire-junit-platform</artifactId>
|
||||
<version>3.0.0-M4</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.jetbrains.kotlin</groupId>
|
||||
<artifactId>kotlin-maven-plugin</artifactId>
|
||||
<version>${kotlin.version}</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>compile</id>
|
||||
<phase>compile</phase>
|
||||
<goals>
|
||||
<goal>compile</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>test-compile</id>
|
||||
<phase>test-compile</phase>
|
||||
<goals>
|
||||
<goal>test-compile</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
<configuration>
|
||||
<jvmTarget>${java.version}</jvmTarget>
|
||||
<args>
|
||||
<arg>-Xinline-classes</arg>
|
||||
</args>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-shade-plugin</artifactId>
|
||||
<version>3.2.4</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>shade</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<createDependencyReducedPom>false</createDependencyReducedPom>
|
||||
<minimizeJar>true</minimizeJar>
|
||||
<transformers>
|
||||
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
|
||||
<mainClass>${main.class}</mainClass>
|
||||
</transformer>
|
||||
</transformers>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-antrun-plugin</artifactId>
|
||||
<version>1.7</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>ktlint</id>
|
||||
<phase>verify</phase>
|
||||
<configuration>
|
||||
<target name="ktlint">
|
||||
<java classname="com.pinterest.ktlint.Main" classpathref="maven.plugin.classpath" dir="${basedir}" failonerror="true" fork="true" taskname="ktlint">
|
||||
<arg value="src/**/*.kt"/>
|
||||
</java>
|
||||
</target>
|
||||
</configuration>
|
||||
<goals>
|
||||
<goal>run</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>ktlint-format</id>
|
||||
<configuration>
|
||||
<target name="ktlint">
|
||||
<java classname="com.pinterest.ktlint.Main" classpathref="maven.plugin.classpath" dir="${basedir}" failonerror="true" fork="true" taskname="ktlint">
|
||||
<arg value="-F"/>
|
||||
<arg value="src/**/*.kt"/>
|
||||
</java>
|
||||
</target>
|
||||
</configuration>
|
||||
<goals>
|
||||
<goal>run</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.pinterest</groupId>
|
||||
<artifactId>ktlint</artifactId>
|
||||
<version>0.39.0</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
31
src/main/kotlin/ColumnType.kt
Normal file
31
src/main/kotlin/ColumnType.kt
Normal file
@ -0,0 +1,31 @@
|
||||
package be.vandewalleh
|
||||
|
||||
/**
|
||||
@see https://ktorm.liuwj.me/en/schema-definition.html
|
||||
*/
|
||||
@Suppress("unused")
|
||||
enum class ColumnType(val ktormType: String, val kotlinType: String, val sqlType: String) {
|
||||
BOOLEAN("boolean", "Boolean", "boolean"),
|
||||
INT("int", "Int", "int"),
|
||||
SHORT("short", "Short", "smallint"),
|
||||
LONG("long", "Long", "bigint"),
|
||||
FLOAT("float", "Float", "float"),
|
||||
DOUBLE("double", "Double", "double"),
|
||||
DECIMAL("decimal", "BigDecimal", "decimal"),
|
||||
VARCHAR("varchar", "String", "varchar"),
|
||||
TEXT("text", "String", "text"),
|
||||
BLOB("blob", "ByteArray", "blob"),
|
||||
BYTES("bytes", "ByteArray", "bytes"),
|
||||
JDBC_TIMESTAMP("jdbcTimestamp", "Timestamp", "timestamp"),
|
||||
JDBC_DATE("jdbcDate", "Date", "date"),
|
||||
JDBC_TIME("jdbcTime", "Time", "time"),
|
||||
TIMESTAMP("timestamp", "Instant", "timestamp"),
|
||||
DATETIME("datetime", "LocalDateTime", "datetime"),
|
||||
DATE("date", "LocalDate", "date"),
|
||||
TIME("time", "Time", "time"),
|
||||
MONTHDAY("monthDay", "MonthDay", "varchar"),
|
||||
YEARMONTH("yearMonth", "YearMonth", "varchar"),
|
||||
YEAR("year", "Year", "int"),
|
||||
ENUM("enum", "Enum", "enum"),
|
||||
UUID("uuid", "UUID", "uuid"),
|
||||
}
|
||||
59
src/main/kotlin/GeneratorCommand.kt
Normal file
59
src/main/kotlin/GeneratorCommand.kt
Normal file
@ -0,0 +1,59 @@
|
||||
package be.vandewalleh
|
||||
|
||||
import com.github.ajalt.clikt.core.CliktCommand
|
||||
import com.github.ajalt.clikt.core.UsageError
|
||||
import java.io.File
|
||||
|
||||
class GeneratorCommand(private val generator: TableGenerator) : CliktCommand() {
|
||||
private fun promptReference(): Reference? {
|
||||
if (!(prompt("Reference", default = "false") { it.toBoolean() } ?: return null)) return null
|
||||
|
||||
val tableName = prompt("Table name") { TableName(it) } ?: return null
|
||||
val referenceName = prompt("Reference Name") { ReferenceName(it) } ?: return null
|
||||
val entityName = prompt("Entity Name") { EntityName(it) } ?: return null
|
||||
|
||||
return Reference(tableName, referenceName, entityName)
|
||||
}
|
||||
|
||||
private fun promptColumn(): Column? {
|
||||
val name = prompt("Column name") { ColumnName(it) } ?: return null
|
||||
println(ColumnType.values().joinToString("|") { it.name.toLowerCase() })
|
||||
|
||||
val type = prompt("Column type") { input ->
|
||||
enumValues<ColumnType>().find { it.name.equals(input, ignoreCase = true) }
|
||||
} ?: return null
|
||||
|
||||
val nullable = prompt("Nullable", default = "false") { it.toBoolean() } ?: return null
|
||||
val primaryKey = prompt("Primary key", default = "false") { it.toBoolean() } ?: return null
|
||||
val reference = promptReference()
|
||||
|
||||
return Column(name, type, nullable, primaryKey, reference)
|
||||
}
|
||||
|
||||
override fun run() {
|
||||
val tableName = prompt("Table name") { TableName(it) } ?: throw UsageError("Table Name Required")
|
||||
|
||||
val defaultEntityName = when {
|
||||
tableName.value.endsWith("s") -> tableName.value.removeSuffix("s") + "Entity"
|
||||
tableName.value.endsWith("Table") -> tableName.value.removeSuffix("Table") + "Entity"
|
||||
else -> null
|
||||
}
|
||||
|
||||
val entityName = prompt("Entity name", default = defaultEntityName) {
|
||||
EntityName(it)
|
||||
} ?: throw UsageError("Entity Name Required")
|
||||
|
||||
val columns = generateSequence {
|
||||
println()
|
||||
promptColumn()
|
||||
}.toList()
|
||||
|
||||
val generated = generator(Table(tableName, entityName, columns))
|
||||
|
||||
val fileName = tableName.value + ".kt"
|
||||
|
||||
File(fileName).writeText(generated)
|
||||
|
||||
println("\nGenerated output in $fileName")
|
||||
}
|
||||
}
|
||||
27
src/main/kotlin/Ktormgenerator.kt
Normal file
27
src/main/kotlin/Ktormgenerator.kt
Normal file
@ -0,0 +1,27 @@
|
||||
package be.vandewalleh
|
||||
|
||||
data class Table(val name: TableName, val entityName: EntityName, val columns: List<Column>)
|
||||
|
||||
data class Column(
|
||||
val name: ColumnName,
|
||||
val type: ColumnType,
|
||||
val nullable: Boolean,
|
||||
val primaryKey: Boolean,
|
||||
val reference: Reference? = null,
|
||||
)
|
||||
|
||||
data class Reference(
|
||||
val tableName: TableName,
|
||||
val referenceName: ReferenceName,
|
||||
val entityName: EntityName,
|
||||
)
|
||||
|
||||
inline class EntityName(val value: String)
|
||||
inline class ColumnName(val value: String)
|
||||
inline class ReferenceName(val value: String)
|
||||
inline class TableName(val value: String)
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
val generator = TableGenerator()
|
||||
GeneratorCommand(generator).main(args)
|
||||
}
|
||||
41
src/main/kotlin/TableGenerator.kt
Normal file
41
src/main/kotlin/TableGenerator.kt
Normal file
@ -0,0 +1,41 @@
|
||||
package be.vandewalleh
|
||||
|
||||
class TableGenerator {
|
||||
operator fun invoke(table: Table): String {
|
||||
|
||||
val (tableName, entityName, columns) = table
|
||||
|
||||
val columnTemplate: (Column) -> String = { (name, type, _, primaryKey, reference) ->
|
||||
buildString {
|
||||
append("val ${name.value} = ${type.ktormType}(\"${name.value}\")")
|
||||
if (primaryKey) append(".primaryKey()")
|
||||
if (reference != null)
|
||||
append(".references(${reference.tableName.value}) { it.${reference.referenceName.value} }")
|
||||
else
|
||||
append(".bindTo { it.${name.value} }")
|
||||
}
|
||||
}
|
||||
|
||||
val entityColumnTemplate: (Column) -> String = { (name, type, nullable, _, reference) ->
|
||||
buildString {
|
||||
if (reference != null) append("var ${reference.referenceName.value}: ${reference.entityName.value}")
|
||||
else append("var ${name.value}: ${type.kotlinType}")
|
||||
|
||||
if (nullable) append("?")
|
||||
}
|
||||
}
|
||||
|
||||
return buildString {
|
||||
append("internal open class ${tableName.value}(alias: String?) ")
|
||||
append(": Table<${entityName.value}>(\"${tableName.value}\", alias) {\n")
|
||||
append(" companion object : ${tableName.value}(null)\n\n")
|
||||
append(" override fun aliased(alias: String) = ${tableName.value}(alias)\n\n")
|
||||
append(" ${columns.joinToString("\n ") { columnTemplate(it) }}\n")
|
||||
append("}\n\n")
|
||||
append("internal interface ${entityName.value} : Entity<${entityName.value}> {\n")
|
||||
append(" companion object : Entity.Factory<${entityName.value}>()\n\n")
|
||||
append(" ${columns.joinToString("\n ") { entityColumnTemplate(it) }}\n")
|
||||
append("}\n")
|
||||
}
|
||||
}
|
||||
}
|
||||
122
src/test/kotlin/TableGeneratorTest.kt
Normal file
122
src/test/kotlin/TableGeneratorTest.kt
Normal file
@ -0,0 +1,122 @@
|
||||
package be.vandewalleh
|
||||
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.junit.jupiter.api.Test
|
||||
|
||||
internal class TableGeneratorTest {
|
||||
|
||||
private val generator = TableGenerator()
|
||||
|
||||
@Test
|
||||
fun generateUsers() {
|
||||
val res = generator(
|
||||
Table(
|
||||
TableName("Users"), EntityName("UserEntity"),
|
||||
listOf(
|
||||
Column(name = ColumnName("id"), type = ColumnType.INT, nullable = false, primaryKey = true),
|
||||
Column(
|
||||
name = ColumnName("username"), type = ColumnType.VARCHAR,
|
||||
nullable = false, primaryKey = false
|
||||
),
|
||||
Column(
|
||||
name = ColumnName("password"), type = ColumnType.VARCHAR,
|
||||
nullable = true, primaryKey = false
|
||||
),
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
val expected = """
|
||||
internal open class Users(alias: String?) : Table<UserEntity>("Users", alias) {
|
||||
companion object : Users(null)
|
||||
|
||||
override fun aliased(alias: String) = Users(alias)
|
||||
|
||||
val id = int("id").primaryKey().bindTo { it.id }
|
||||
val username = varchar("username").bindTo { it.username }
|
||||
val password = varchar("password").bindTo { it.password }
|
||||
}
|
||||
|
||||
internal interface UserEntity : Entity<UserEntity> {
|
||||
companion object : Entity.Factory<UserEntity>()
|
||||
|
||||
var id: Int
|
||||
var username: String
|
||||
var password: String?
|
||||
}
|
||||
|
||||
""".trimIndent()
|
||||
|
||||
assertThat(res).isEqualTo(expected)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun generateNotes() {
|
||||
val res = generator(
|
||||
Table(
|
||||
TableName("Notes"), EntityName("NoteEntity"),
|
||||
listOf(
|
||||
Column(name = ColumnName("uuid"), type = ColumnType.UUID, nullable = false, primaryKey = true),
|
||||
Column(name = ColumnName("title"), type = ColumnType.VARCHAR, nullable = false, primaryKey = false),
|
||||
Column(name = ColumnName("markdown"), type = ColumnType.TEXT, nullable = false, primaryKey = false),
|
||||
Column(name = ColumnName("html"), type = ColumnType.TEXT, nullable = false, primaryKey = false),
|
||||
Column(
|
||||
name = ColumnName("user_id"), type = ColumnType.INT, nullable = false, primaryKey = false,
|
||||
reference = Reference(TableName("Users"), ReferenceName("user"), EntityName("User"))
|
||||
),
|
||||
Column(
|
||||
name = ColumnName("updated_at"), type = ColumnType.DATETIME,
|
||||
nullable = false, primaryKey = false
|
||||
),
|
||||
Column(
|
||||
name = ColumnName("updated_at"), type = ColumnType.DATETIME,
|
||||
nullable = false, primaryKey = false
|
||||
),
|
||||
Column(
|
||||
name = ColumnName("deleted"), type = ColumnType.BOOLEAN,
|
||||
nullable = false, primaryKey = false
|
||||
),
|
||||
Column(
|
||||
name = ColumnName("public"), type = ColumnType.BOOLEAN,
|
||||
nullable = false, primaryKey = false
|
||||
),
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
val expected = """
|
||||
internal open class Notes(alias: String?) : Table<NoteEntity>("Notes", alias) {
|
||||
companion object : Notes(null)
|
||||
|
||||
override fun aliased(alias: String) = Notes(alias)
|
||||
|
||||
val uuid = uuid("uuid").primaryKey().bindTo { it.uuid }
|
||||
val title = varchar("title").bindTo { it.title }
|
||||
val markdown = text("markdown").bindTo { it.markdown }
|
||||
val html = text("html").bindTo { it.html }
|
||||
val user_id = int("user_id").references(Users) { it.user }
|
||||
val updated_at = datetime("updated_at").bindTo { it.updated_at }
|
||||
val updated_at = datetime("updated_at").bindTo { it.updated_at }
|
||||
val deleted = boolean("deleted").bindTo { it.deleted }
|
||||
val public = boolean("public").bindTo { it.public }
|
||||
}
|
||||
|
||||
internal interface NoteEntity : Entity<NoteEntity> {
|
||||
companion object : Entity.Factory<NoteEntity>()
|
||||
|
||||
var uuid: UUID
|
||||
var title: String
|
||||
var markdown: String
|
||||
var html: String
|
||||
var user: User
|
||||
var updated_at: LocalDateTime
|
||||
var updated_at: LocalDateTime
|
||||
var deleted: Boolean
|
||||
var public: Boolean
|
||||
}
|
||||
|
||||
""".trimIndent()
|
||||
|
||||
assertThat(res).isEqualTo(expected)
|
||||
}
|
||||
}
|
||||
4
src/test/resources/junit-platform.properties
Normal file
4
src/test/resources/junit-platform.properties
Normal file
@ -0,0 +1,4 @@
|
||||
junit.jupiter.testinstance.lifecycle.default=per_class
|
||||
junit.jupiter.execution.parallel.enabled=true
|
||||
junit.jupiter.execution.parallel.mode.default=same_thread
|
||||
junit.jupiter.execution.parallel.mode.classes.default=concurrent
|
||||
Loading…
x
Reference in New Issue
Block a user