
commonMain.buildCsv.kt Maven / Gradle / Ivy
/** Copyright 2023 Halfbit GmbH, Sergej Shafarenka */
package de.halfbit.csv
import de.halfbit.csv.BaseCsv.Row
import de.halfbit.csv.Csv.DataRow
import de.halfbit.csv.Csv.HeaderRow
import kotlin.math.max
@DslMarker
public annotation class CsvDsl
public fun buildCsv(
block: CsvBuilder.() -> Unit
): Csv {
val header = mutableListOf()
val data = mutableListOf()
block(CsvBuilder(header, data))
val headerRow = header.getOrNull(0) ?: DefaultHeaderRow(emptyList())
return Csv(headerRow, data)
}
@CsvDsl
public class CsvBuilder internal constructor(
private val header: MutableList,
private val data: MutableList,
) {
public fun row(block: CsvRowBuilder.() -> Unit) {
val row = mutableListOf()
val scope = CsvRowBuilder(row)
block(scope)
if (header.isEmpty()) {
header += DefaultHeaderRow(row)
} else {
data += DefaultDataRow(row, header[0])
}
}
}
@CsvDsl
public class CsvRowBuilder internal constructor(
private val row: MutableList,
) {
public fun value(value: String) {
row.add(value)
}
}
// implementations
public fun BaseCsv(allRows: List): BaseCsv = object : BaseCsv {
override val allRows: List get() = allRows
override fun toString(): String =
buildString {
allRows.forEach { data ->
append(data)
append("\n")
}
}
}
public fun Csv(
header: HeaderRow,
data: List,
): Csv = object : Csv {
override val header: HeaderRow get() = header
override val data: List get() = data
override val allRows: List by lazy {
if (header.isEmpty()) data else listOf(header) + data
}
override fun toString(): String =
buildString {
append(header)
append("\n")
data.forEach { data ->
append(data)
append("\n")
}
}
}
internal open class DefaultRow(protected val row: List) : Row, List by row {
override fun replaceValue(valueIndex: Int, newValue: String): Row {
val thisRow = this
val newRow = buildList {
for (index in 0..max(thisRow.size, valueIndex)) {
if (index == valueIndex) {
add(newValue)
} else {
add(thisRow.getOrNull(index) ?: "")
}
}
}
return DefaultRow(newRow)
}
override fun toString(): String = row.toString()
}
internal class DefaultHeaderRow(row: List) : HeaderRow, DefaultRow(row) {
private val indexOfColumn: MutableMap by lazy { mutableMapOf() }
override fun indexOfColumn(name: String): Int =
indexOfColumn[name] ?: run {
val index = indexOf(name)
indexOfColumn[name] = index
index
}
}
internal class DefaultDataRow(
row: List,
private val header: HeaderRow
) : DataRow, DefaultRow(row) {
override fun value(columnName: String): String =
row.getOrNull(header.indexOfColumn(columnName)) ?: ""
override fun replaceValue(columnName: String, newValue: String): DataRow {
val replaceIndex = header.indexOfColumn(columnName)
if (replaceIndex < 0) return this
val newRow = buildList {
for (index in 0..max(row.lastIndex, replaceIndex)) {
if (index == replaceIndex) {
add(newValue)
} else {
add(row.getOrNull(index) ?: "")
}
}
}
return DefaultDataRow(newRow, header)
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy