Skip to content

Commit

Permalink
docs: generate type converter supported types tables from actual type…
Browse files Browse the repository at this point in the history
… converters and include the docs dir as a gradle module
  • Loading branch information
mcarleio committed Apr 5, 2023
1 parent 147b563 commit 16ee9d1
Show file tree
Hide file tree
Showing 14 changed files with 455 additions and 57 deletions.
2 changes: 1 addition & 1 deletion docs/Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -87,4 +87,4 @@ DEPENDENCIES
just-the-docs (= 0.4.2)

BUNDLED WITH
2.3.9
2.3.25
6 changes: 6 additions & 0 deletions docs/_config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,9 @@ color_scheme: wider

plugins:
- jekyll-target-blank

exclude:
- src/
- build.gradle.kts
- build/
- typeconverter/gen
30 changes: 30 additions & 0 deletions docs/_sass/custom/custom.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
@for $i from 0 through 4 {
$start: 120;
$base: 10;
$columnWidth: $start + $base * $i;
table.fixed-first-column-#{$columnWidth} {
thead th:first-child {
min-width: $columnWidth + 0px;
max-width: $columnWidth + 0px;
width: $columnWidth + 0px;
position: absolute;
}

tbody td:first-child {
min-width: $columnWidth + 0px;
max-width: $columnWidth + 0px;
width: $columnWidth + 0px;
position: absolute;
}

tbody td:nth-child(2) {
margin-left: $columnWidth + 0px;
display: block;
}

thead th:nth-child(2) {
margin-left: $columnWidth + 0px;
display: block;
}
}
}
20 changes: 20 additions & 0 deletions docs/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
plugins {
id("konvert.kotlin")
}

dependencies {
api(project(":converter"))

// Generate docs...
implementation("net.steppschuh.markdowngenerator:markdowngenerator:1.3.1.1")
}

val generateMarkdownTablesTask = tasks.create<JavaExec>("generateMarkdownTables") {
classpath = sourceSets.main.get().runtimeClasspath

mainClass.set("io.mcarle.konvert.internal.docs.GenerateDocumentationKt")
}

tasks.named("build") {
dependsOn += generateMarkdownTablesTask
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,283 @@
package io.mcarle.konvert.internal.docs

import io.mcarle.konvert.converter.*
import io.mcarle.konvert.converter.api.TypeConverter
import net.steppschuh.markdowngenerator.table.Table
import net.steppschuh.markdowngenerator.text.emphasis.BoldText
import java.nio.file.Files
import java.nio.file.Path
import java.nio.file.Paths
import java.util.Date
import java.util.ServiceLoader
import kotlin.io.path.createDirectories
import kotlin.reflect.KClass

fun main() {
val list = ServiceLoader.load(TypeConverter::class.java).toList()

val outputDir = Paths.get("typeconverter", "gen")
outputDir.createDirectories()

val basicTypesList = generateBasicTypeConverterTable(outputDir, list)
generateTemporalTypeConverterTable(outputDir, list, basicTypesList)
generateIterablesTypeConverterTable(outputDir, list)
generateEnumTypeConverterTable(outputDir, list, basicTypesList)
generateMapTypeConverterTable(outputDir, list)
}

const val FROM_TO = "From\\To"

fun generateBasicTypeConverterTable(outputDir: Path, typeConverters: List<TypeConverter>): List<KClass<*>> {
val basicTypeConverters = typeConverters
.filterIsInstance<BaseTypeConverter>()
.sortedWith(kotlin.comparisons.compareBy({ it.sourceClass.simpleName }, { it.targetClass.simpleName }))

val basicClasses = (basicTypeConverters.flatMap { listOf(it.sourceClass, it.targetClass) })
.distinct()
.sortedBy { it.simpleName }

val builder = Table.Builder()
.withAlignments(Table.ALIGN_CENTER, *basicClasses.map { Table.ALIGN_CENTER }.toTypedArray())
.addRow(FROM_TO, *basicClasses.map { it.simpleName }.toTypedArray())

basicClasses.forEachIndexed { index, clazz ->
builder.addRow(
BoldText(clazz.simpleName),
*basicTypeConverters.filter { it.sourceClass == clazz }
.map { if (it.enabledByDefault) "" else "" }
.toMutableList().also {
it.add(index, "")
}
.toTypedArray()
)
}

Files.writeString(outputDir.resolve("basic.md"), builder.build().toString() + "\n{: .fixed-first-column-120 }")

return basicClasses
}

fun generateTemporalTypeConverterTable(outputDir: Path, typeConverters: List<TypeConverter>, basicTypesList: List<KClass<*>>) {
val map = mutableMapOf<DescriptiveClass, MutableMap<DescriptiveClass, Boolean>>()
typeConverters.filterIsInstance<TemporalToTemporalConverter>().forEach {
map.getOrPut(DescriptiveClass.from(it, it.sourceClass)) { mutableMapOf() }[DescriptiveClass.to(it, it.targetClass)] =
it.enabledByDefault
}
typeConverters.filterIsInstance<TemporalToXConverter>().forEach {
map.getOrPut(DescriptiveClass.from(it, it.sourceClass)) { mutableMapOf() }[DescriptiveClass.to(it, it.targetClass)] =
it.enabledByDefault
}
typeConverters.filterIsInstance<XToTemporalConverter>().forEach {
map.getOrPut(DescriptiveClass.from(it, it.sourceClass)) { mutableMapOf() }[DescriptiveClass.to(it, it.targetClass)] =
it.enabledByDefault
}
typeConverters.filterIsInstance<DateToTemporalConverter>().forEach {
map.getOrPut(DescriptiveClass.from(it, Date::class)) { mutableMapOf() }[DescriptiveClass.to(it, it.targetClass)] =
it.enabledByDefault
}
typeConverters.filterIsInstance<DateToXConverter>().forEach {
map.getOrPut(DescriptiveClass.from(it, Date::class)) { mutableMapOf() }[DescriptiveClass.to(it, it.targetClass)] =
it.enabledByDefault
}
typeConverters.filterIsInstance<TemporalToDateConverter>().forEach {
map.getOrPut(DescriptiveClass.from(it, it.sourceClass)) { mutableMapOf() }[DescriptiveClass.to(it, Date::class)] =
it.enabledByDefault
}
typeConverters.filterIsInstance<XToDateConverter>().forEach {
map.getOrPut(DescriptiveClass.from(it, it.sourceClass)) { mutableMapOf() }[DescriptiveClass.to(it, Date::class)] =
it.enabledByDefault
}

val from = map.keys.sortedWith(compareBy({ it.clazz.simpleName }, { it.description }))
val to = map.values.flatMap { it.keys }.distinct().sortedWith(compareBy({ it.clazz.simpleName }, { it.description }))

val toWithoutBasic = to.filterNot { it.clazz in basicTypesList }

val builder = Table.Builder()
.withAlignments(Table.ALIGN_CENTER, *toWithoutBasic.map { Table.ALIGN_CENTER }.toTypedArray())
.addRow(FROM_TO, *toWithoutBasic.map { it.toString() }.toTypedArray())

from.forEach { fromClass ->
builder.addRow(
BoldText(fromClass.toString()),
*toWithoutBasic.map { toClass ->
if (fromClass == toClass) return@map ""
when (map[fromClass]!![toClass]) {
null -> null
true -> ""
false -> ""
}
}.toTypedArray()
)

}

Files.writeString(outputDir.resolve("to_temporal.md"), builder.build().toString() + "\n{: .fixed-first-column-140 }")

val fromWithoutBasic = from.filterNot { it.clazz in basicTypesList }
val toOnlyBasic = to.filter { it.clazz in basicTypesList }

val builder2 = Table.Builder()
.withAlignments(Table.ALIGN_CENTER, *toOnlyBasic.map { Table.ALIGN_CENTER }.toTypedArray())
.addRow(FROM_TO, *toOnlyBasic.map { it.toString() }.toTypedArray())

fromWithoutBasic.forEach { fromClass ->
builder2.addRow(
BoldText(fromClass.toString()),
*toOnlyBasic.map { toClass ->
if (fromClass == toClass) return@map ""
when (map[fromClass]!![toClass]) {
null -> null
true -> ""
false -> ""
}
}.toTypedArray()
)

}

Files.writeString(
outputDir.resolve("from_temporal.md"),
builder2.build().toString() + "\n{: .fixed-first-column-140 }"
)
}

fun generateIterablesTypeConverterTable(outputDir: Path, typeConverters: List<TypeConverter>) {
val iterableToIterableConverter = typeConverters
.filterIsInstance<IterableToIterableConverter>()
.first()

val supportedIterables = IterableToIterableConverter.supported().map { it.substringAfterLast(".") }.sorted()

val builder = Table.Builder()
.withAlignments(Table.ALIGN_CENTER, *supportedIterables.map { Table.ALIGN_CENTER }.toTypedArray())
.addRow(FROM_TO, *supportedIterables.toTypedArray())

supportedIterables.forEach { clazzName ->
builder.addRow(
BoldText(clazzName),
*supportedIterables
.map { if (iterableToIterableConverter.enabledByDefault) "" else "" }
.toTypedArray()
)
}

Files.writeString(outputDir.resolve("iterable.md"), builder.build().toString() + "\n{: .fixed-first-column-160 }")
}
fun generateMapTypeConverterTable(outputDir: Path, typeConverters: List<TypeConverter>) {
val mapToMapConverter = typeConverters
.filterIsInstance<MapToMapConverter>()
.first()

val supportedMaps = MapToMapConverter.supported().map { it.substringAfterLast(".") }.sorted()

val builder = Table.Builder()
.withAlignments(Table.ALIGN_CENTER, *supportedMaps.map { Table.ALIGN_CENTER }.toTypedArray())
.addRow(FROM_TO, *supportedMaps.toTypedArray())

supportedMaps.forEach { clazzName ->
builder.addRow(
BoldText(clazzName),
*supportedMaps
.map { if (mapToMapConverter.enabledByDefault) "" else "" }
.toTypedArray()
)
}

Files.writeString(outputDir.resolve("map.md"), builder.build().toString() + "\n{: .fixed-first-column-140 }")
}

fun generateEnumTypeConverterTable(outputDir: Path, typeConverters: List<TypeConverter>, basicTypesList: List<KClass<*>>) {
val map = mutableMapOf<DescriptiveClass, MutableMap<DescriptiveClass, Boolean>>()
typeConverters.filterIsInstance<EnumToEnumConverter>().forEach {
map.getOrPut(DescriptiveClass.from(it, Enum::class)) { mutableMapOf() }[DescriptiveClass.to(it, Enum::class)] = it.enabledByDefault
}
typeConverters.filterIsInstance<EnumToXConverter>().forEach {
map.getOrPut(DescriptiveClass.from(it, Enum::class)) { mutableMapOf() }[DescriptiveClass.to(it, it.targetClass)] = it.enabledByDefault
}
typeConverters.filterIsInstance<XToEnumConverter>().forEach {
map.getOrPut(DescriptiveClass.from(it, it.sourceClass)) { mutableMapOf() }[DescriptiveClass.to(it, Enum::class)] = it.enabledByDefault
}

val from = map.keys.sortedWith(compareBy({ it.clazz.simpleName }, { it.description }))
val to = map.values.flatMap { it.keys }.distinct().sortedWith(compareBy({ it.clazz.simpleName }, { it.description }))

val toWithoutBasic = to.filterNot { it.clazz in basicTypesList }

val builder = Table.Builder()
.withAlignments(Table.ALIGN_CENTER, *toWithoutBasic.map { Table.ALIGN_CENTER }.toTypedArray())
.addRow(FROM_TO, *toWithoutBasic.map { it.toString() }.toTypedArray())

from.forEach { fromClass ->
builder.addRow(
BoldText(fromClass.toString()),
*toWithoutBasic.map { toClass ->
when (map[fromClass]!![toClass]) {
null -> null
true -> ""
false -> ""
}
}.toTypedArray()
)
}

Files.writeString(outputDir.resolve("to_enum.md"), builder.build().toString() + "\n{: .fixed-first-column-120 }")

val fromWithoutBasic = from.filterNot { it.clazz in basicTypesList }
val toWithoutEnum = to.filter { it.clazz != Enum::class }

val builder2 = Table.Builder()
.withAlignments(Table.ALIGN_CENTER, *toWithoutEnum.map { Table.ALIGN_CENTER }.toTypedArray())
.addRow(FROM_TO, *toWithoutEnum.map { it.toString() }.toTypedArray())

fromWithoutBasic.forEach { fromClass ->
builder2.addRow(
BoldText(fromClass.toString()),
*toWithoutEnum.map { toClass ->
when (map[fromClass]!![toClass]) {
null -> null
true -> ""
false -> ""
}
}.toTypedArray()
)
}

Files.writeString(outputDir.resolve("from_enum.md"), builder2.build().toString() + "\n{: .fixed-first-column-120 }")

}

data class DescriptiveClass(val description: String?, val clazz: KClass<*>) {

companion object {
fun from(converter: Any, sourceClass: KClass<*>) = DescriptiveClass(
when (converter) {
is LongEpochMillisToDateConverter -> "epoch ms"
is LongEpochMillisToInstantConverter -> "epoch ms"
is LongEpochSecondsToDateConverter -> "epoch s"
is LongEpochSecondsToInstantConverter -> "epoch s"
else -> null
},
sourceClass
)

fun to(converter: Any, targetClazz: KClass<*>) = DescriptiveClass(
when (converter) {
is DateToLongEpochMillisConverter -> "epoch ms"
is InstantToLongEpochMillisConverter -> "epoch ms"
is OffsetDateTimeToLongEpochMillisConverter -> "epoch ms"
is ZonedDateTimeToLongEpochMillisConverter -> "epoch ms"
is DateToLongEpochSecondsConverter -> "epoch s"
is InstantToLongEpochSecondsConverter -> "epoch s"
is OffsetDateTimeToLongEpochSecondsConverter -> "epoch s"
is ZonedDateTimeToLongEpochSecondsConverter -> "epoch s"
else -> null
},
targetClazz
)
}

override fun toString(): String {
return clazz.simpleName + if (description != null) " ($description)" else ""
}
}
17 changes: 17 additions & 0 deletions docs/typeconverter/gen/basic.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
| From\To | Boolean | Byte | Char | Double | Float | Int | Long | Number | Short | String | UByte | UInt | ULong | UShort |
|:-----------:|:-------:|:----:|:----:|:------:|:-----:|:---:|:----:|:------:|:-----:|:------:|:-----:|:----:|:-----:|:------:|
| **Boolean** |||||||||||||||
| **Byte** |||||||||||||||
| **Char** |||||||||||||||
| **Double** |||||||||||||||
| **Float** |||||||||||||||
| **Int** |||||||||||||||
| **Long** |||||||||||||||
| **Number** |||||||||||||||
| **Short** |||||||||||||||
| **String** |||||||||||||||
| **UByte** |||||||||||||||
| **UInt** |||||||||||||||
| **ULong** |||||||||||||||
| **UShort** |||||||||||||||
{: .fixed-first-column-120 }
4 changes: 4 additions & 0 deletions docs/typeconverter/gen/from_enum.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
| From\To | Byte | Char | Double | Float | Int | Long | Number | Short | String | UByte | UInt | ULong | UShort |
|:--------:|:----:|:----:|:------:|:-----:|:---:|:----:|:------:|:-----:|:------:|:-----:|:----:|:-----:|:------:|
| **Enum** ||||||||||||||
{: .fixed-first-column-120 }
11 changes: 11 additions & 0 deletions docs/typeconverter/gen/from_temporal.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
| From\To | Long (epoch ms) | Long (epoch s) | String |
|:------------------:|:---------------:|:--------------:|:------:|
| **Date** ||||
| **Instant** ||||
| **LocalDate** | | ||
| **LocalDateTime** | | ||
| **LocalTime** | | ||
| **OffsetDateTime** ||||
| **OffsetTime** | | ||
| **ZonedDateTime** ||||
{: .fixed-first-column-140 }
Loading

0 comments on commit 16ee9d1

Please sign in to comment.