diff --git a/dataforge-io/src/commonMain/kotlin/hep/dataforge/io/Envelope.kt b/dataforge-io/src/commonMain/kotlin/hep/dataforge/io/Envelope.kt
index adffdbf7..d7c60116 100644
--- a/dataforge-io/src/commonMain/kotlin/hep/dataforge/io/Envelope.kt
+++ b/dataforge-io/src/commonMain/kotlin/hep/dataforge/io/Envelope.kt
@@ -28,7 +28,7 @@ interface Envelope {
/**
* Build a static envelope using provided builder
*/
- operator fun invoke(block: EnvelopeBuilder.() -> Unit) = EnvelopeBuilder().apply(block).build()
+ inline operator fun invoke(block: EnvelopeBuilder.() -> Unit) = EnvelopeBuilder().apply(block).build()
}
}
diff --git a/dataforge-io/src/commonMain/kotlin/hep/dataforge/io/EnvelopeBuilder.kt b/dataforge-io/src/commonMain/kotlin/hep/dataforge/io/EnvelopeBuilder.kt
index 0f12c213..94f4e59d 100644
--- a/dataforge-io/src/commonMain/kotlin/hep/dataforge/io/EnvelopeBuilder.kt
+++ b/dataforge-io/src/commonMain/kotlin/hep/dataforge/io/EnvelopeBuilder.kt
@@ -18,8 +18,15 @@ class EnvelopeBuilder {
metaBuilder.update(meta)
}
+ /**
+ * The general purpose of the envelope
+ */
var type by metaBuilder.string(key = Envelope.ENVELOPE_TYPE_KEY)
var dataType by metaBuilder.string(key = Envelope.ENVELOPE_DATA_TYPE_KEY)
+
+ /**
+ * Data unique identifier to bypass identity checks
+ */
var dataID by metaBuilder.string(key = Envelope.ENVELOPE_DATA_ID_KEY)
var description by metaBuilder.string(key = Envelope.ENVELOPE_DESCRIPTION_KEY)
var name by metaBuilder.string(key = Envelope.ENVELOPE_NAME_KEY)
@@ -32,5 +39,14 @@ class EnvelopeBuilder {
data = ArrayBinary.write(builder = block)
}
- internal fun build() = SimpleEnvelope(metaBuilder.seal(), data)
-}
\ No newline at end of file
+ fun build() = SimpleEnvelope(metaBuilder.seal(), data)
+
+}
+
+//@ExperimentalContracts
+//suspend fun EnvelopeBuilder.buildData(block: suspend Output.() -> Unit): Binary{
+// contract {
+// callsInPlace(block, InvocationKind.EXACTLY_ONCE)
+// }
+// val scope = CoroutineScope(coroutineContext)
+//}
\ No newline at end of file
diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MutableMeta.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MutableMeta.kt
index 88819f2c..043283e3 100644
--- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MutableMeta.kt
+++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MutableMeta.kt
@@ -115,6 +115,8 @@ operator fun MutableMeta<*>.set(name: NameToken, value: Any?) = set(name.asName(
operator fun MutableMeta<*>.set(key: String, value: Any?) = set(key.toName(), value)
+operator fun MutableMeta<*>.set(key: String, index: String, value: Any?) = set(key.toName().withIndex(index), value)
+
/**
* Update existing mutable node with another node. The rules are following:
* * value replaces anything
@@ -161,7 +163,7 @@ operator fun MutableMeta<*>.set(name: String, metas: Iterable): Unit = set
/**
* Append the node with a same-name-sibling, automatically generating numerical index
*/
-fun > M.append(name: Name, value: Any?) {
+fun > M.append(name: Name, value: Any?) {
require(!name.isEmpty()) { "Name could not be empty for append operation" }
val newIndex = name.last()!!.index
if (newIndex.isNotEmpty()) {
@@ -172,4 +174,4 @@ fun > M.append(name: Name, value: Any?) {
}
}
-fun > M.append(name: String, value: Any?) = append(name.toName(), value)
\ No newline at end of file
+fun > M.append(name: String, value: Any?) = append(name.toName(), value)
\ No newline at end of file
diff --git a/dataforge-tables/src/commonMain/kotlin/hep/dataforge/tables/ColumnHeader.kt b/dataforge-tables/src/commonMain/kotlin/hep/dataforge/tables/ColumnHeader.kt
new file mode 100644
index 00000000..2023d11b
--- /dev/null
+++ b/dataforge-tables/src/commonMain/kotlin/hep/dataforge/tables/ColumnHeader.kt
@@ -0,0 +1,36 @@
+package hep.dataforge.tables
+
+import hep.dataforge.meta.Meta
+import hep.dataforge.meta.get
+import hep.dataforge.meta.int
+import hep.dataforge.meta.string
+import hep.dataforge.values.Value
+import hep.dataforge.values.ValueType
+import kotlin.reflect.KClass
+
+typealias TableHeader = List>
+
+typealias ValueTableHeader = List>
+
+interface ColumnHeader {
+ val name: String
+ val type: KClass
+ val meta: Meta
+}
+
+data class SimpleColumnHeader(
+ override val name: String,
+ override val type: KClass,
+ override val meta: Meta
+) : ColumnHeader
+
+val ColumnHeader.valueType: ValueType? get() = meta["valueType"].string?.let { ValueType.valueOf(it) }
+
+val ColumnHeader.textWidth: Int
+ get() = meta["columnWidth"].int ?: when (valueType) {
+ ValueType.NUMBER -> 8
+ ValueType.STRING -> 16
+ ValueType.BOOLEAN -> 5
+ ValueType.NULL -> 5
+ null -> 16
+ }
diff --git a/dataforge-tables/src/commonMain/kotlin/hep/dataforge/tables/ColumnTable.kt b/dataforge-tables/src/commonMain/kotlin/hep/dataforge/tables/ColumnTable.kt
index c48b30f8..dbb90cf2 100644
--- a/dataforge-tables/src/commonMain/kotlin/hep/dataforge/tables/ColumnTable.kt
+++ b/dataforge-tables/src/commonMain/kotlin/hep/dataforge/tables/ColumnTable.kt
@@ -12,16 +12,20 @@ class ColumnTable(override val columns: Collection>) : Table<
require(columns.all { it.size == rowsNum }) { "All columns must be of the same size" }
}
- override val rows: List
+ override val rows: List>
get() = (0 until rowsNum).map { VirtualRow(this, it) }
- override fun getValue(row: Int, column: String, type: KClass): T? {
+ override fun getValue(row: Int, column: String, type: KClass): T? {
val value = columns[column]?.get(row)
return type.cast(value)
}
}
-internal class VirtualRow(val table: Table, val index: Int) : Row {
- override fun getValue(column: String, type: KClass): T? = table.getValue(index, column, type)
+internal class VirtualRow(val table: Table, val index: Int) : Row {
+ override fun getValue(column: String, type: KClass): T? = table.getValue(index, column, type)
+
+// override fun get(columnHeader: ColumnHeader): T? {
+// return table.co[columnHeader][index]
+// }
}
diff --git a/dataforge-tables/src/commonMain/kotlin/hep/dataforge/tables/MapRow.kt b/dataforge-tables/src/commonMain/kotlin/hep/dataforge/tables/MapRow.kt
index 421241d4..04c7d3a4 100644
--- a/dataforge-tables/src/commonMain/kotlin/hep/dataforge/tables/MapRow.kt
+++ b/dataforge-tables/src/commonMain/kotlin/hep/dataforge/tables/MapRow.kt
@@ -2,8 +2,8 @@ package hep.dataforge.tables
import kotlin.reflect.KClass
-inline class MapRow(val values: Map) : Row {
- override fun getValue(column: String, type: KClass): T? {
+inline class MapRow(val values: Map) : Row {
+ override fun getValue(column: String, type: KClass): T? {
val value = values[column]
return type.cast(value)
}
diff --git a/dataforge-tables/src/commonMain/kotlin/hep/dataforge/tables/MutableColumnTable.kt b/dataforge-tables/src/commonMain/kotlin/hep/dataforge/tables/MutableColumnTable.kt
index d7a360c9..8a9b06bc 100644
--- a/dataforge-tables/src/commonMain/kotlin/hep/dataforge/tables/MutableColumnTable.kt
+++ b/dataforge-tables/src/commonMain/kotlin/hep/dataforge/tables/MutableColumnTable.kt
@@ -10,11 +10,11 @@ class MutableColumnTable(val size: Int) : Table {
private val _columns = ArrayList>()
override val columns: List> get() = _columns
- override val rows: List get() = (0 until size).map {
+ override val rows: List> get() = (0 until size).map {
VirtualRow(this, it)
}
- override fun getValue(row: Int, column: String, type: KClass): T? {
+ override fun getValue(row: Int, column: String, type: KClass): T? {
val value = columns[column]?.get(row)
return type.cast(value)
}
diff --git a/dataforge-tables/src/commonMain/kotlin/hep/dataforge/tables/MutableTable.kt b/dataforge-tables/src/commonMain/kotlin/hep/dataforge/tables/MutableTable.kt
index 686e3c34..aa2d9abf 100644
--- a/dataforge-tables/src/commonMain/kotlin/hep/dataforge/tables/MutableTable.kt
+++ b/dataforge-tables/src/commonMain/kotlin/hep/dataforge/tables/MutableTable.kt
@@ -1,18 +1,14 @@
package hep.dataforge.tables
import hep.dataforge.meta.Meta
+import hep.dataforge.values.Value
import kotlin.reflect.KClass
-class SimpleColumnHeader(
- override val name: String,
- override val type: KClass,
- override val meta: Meta
-) : ColumnHeader
-
class MutableTable(
- override val rows: MutableList,
+ override val rows: MutableList>,
override val header: MutableList>
-) : RowTable(rows, header) {
+) : RowTable(rows, header) {
+
fun column(name: String, type: KClass, meta: Meta): ColumnHeader {
val column = SimpleColumnHeader(name, type, meta)
header.add(column)
@@ -21,23 +17,24 @@ class MutableTable(
inline fun column(
name: String,
- noinline columnMetaBuilder: ColumnScheme.() -> Unit
+ noinline columnMetaBuilder: ColumnScheme.() -> Unit = {}
): ColumnHeader {
return column(name, T::class, ColumnScheme(columnMetaBuilder).toMeta())
}
- fun row(block: MutableMap.() -> Unit): Row {
- val map = HashMap().apply(block)
+ fun row(map: Map): Row {
val row = MapRow(map)
rows.add(row)
return row
}
- operator fun MutableMap.set(header: ColumnHeader, value: T?) {
- set(header.name, value)
- }
+ fun row(vararg pairs: Pair, T>): Row =
+ row(pairs.associate { it.first.name to it.second })
}
+fun MutableTable.row(vararg pairs: Pair, Any?>): Row =
+ row(pairs.associate { it.first.name to Value.of(it.second) })
+
fun Table.edit(block: MutableTable.() -> Unit): Table {
return MutableTable(rows.toMutableList(), header.toMutableList()).apply(block)
}
\ No newline at end of file
diff --git a/dataforge-tables/src/commonMain/kotlin/hep/dataforge/tables/RowTable.kt b/dataforge-tables/src/commonMain/kotlin/hep/dataforge/tables/RowTable.kt
index 98d7d4e1..c565d9cd 100644
--- a/dataforge-tables/src/commonMain/kotlin/hep/dataforge/tables/RowTable.kt
+++ b/dataforge-tables/src/commonMain/kotlin/hep/dataforge/tables/RowTable.kt
@@ -13,12 +13,11 @@ internal class RowTableColumn(val table: Table, val header: C
override fun get(index: Int): T? = table.rows[index].getValue(name, type)
}
-open class RowTable(override val rows: List, override val header: List>) :
- Table {
- override fun getValue(row: Int, column: String, type: KClass): T? =
+open class RowTable(override val rows: List>, override val header: List>) : Table {
+ override fun getValue(row: Int, column: String, type: KClass): T? =
rows[row].getValue(column, type)
override val columns: List> get() = header.map { RowTableColumn(this, it) }
}
-suspend fun Rows.collect(): Table<*> = this as? Table<*> ?: RowTable(rowFlow().toList(), header)
\ No newline at end of file
+suspend fun Rows.collect(): Table = this as? Table ?: RowTable(rowFlow().toList(), header)
\ No newline at end of file
diff --git a/dataforge-tables/src/commonMain/kotlin/hep/dataforge/tables/Table.kt b/dataforge-tables/src/commonMain/kotlin/hep/dataforge/tables/Table.kt
index 4e8215e0..71469984 100644
--- a/dataforge-tables/src/commonMain/kotlin/hep/dataforge/tables/Table.kt
+++ b/dataforge-tables/src/commonMain/kotlin/hep/dataforge/tables/Table.kt
@@ -1,6 +1,5 @@
package hep.dataforge.tables
-import hep.dataforge.meta.Meta
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.asFlow
import kotlin.reflect.KClass
@@ -15,28 +14,30 @@ internal fun KClass.cast(value: Any?): T? {
}
}
-typealias TableHeader = List>
-
/**
* Finite or infinite row set. Rows are produced in a lazy suspendable [Flow].
* Each row must contain at least all the fields mentioned in [header].
*/
-interface Rows {
- val header: TableHeader<*>
- fun rowFlow(): Flow
+interface Rows {
+ val header: TableHeader
+ fun rowFlow(): Flow>
}
-interface Table : Rows {
- fun getValue(row: Int, column: String, type: KClass): T?
+interface Table : Rows {
+ fun getValue(row: Int, column: String, type: KClass): T?
val columns: Collection>
override val header: TableHeader get() = columns.toList()
- val rows: List
- override fun rowFlow(): Flow = rows.asFlow()
+ val rows: List>
+ override fun rowFlow(): Flow> = rows.asFlow()
/**
* Apply typed query to this table and return lazy [Flow] of resulting rows. The flow could be empty.
*/
//fun select(query: Any): Flow = error("Query of type ${query::class} is not supported by this table")
+ companion object {
+ inline operator fun invoke(block: MutableTable.() -> Unit): Table =
+ MutableTable(arrayListOf(), arrayListOf()).apply(block)
+ }
}
operator fun Collection>.get(name: String): Column<*>? = find { it.name == name }
@@ -44,15 +45,9 @@ operator fun Collection>.get(name: String): Column<*>? = find { it.nam
inline operator fun Table.get(row: Int, column: String): T? =
getValue(row, column, T::class)
-interface ColumnHeader {
- val name: String
- val type: KClass
- val meta: Meta
-}
-
operator fun Table.get(row: Int, column: Column): T? = getValue(row, column.name, column.type)
-interface Column : ColumnHeader {
+interface Column : ColumnHeader {
val size: Int
operator fun get(index: Int): T?
}
@@ -65,9 +60,9 @@ operator fun Column.iterator() = iterator {
}
}
-interface Row {
- fun getValue(column: String, type: KClass): T?
+interface Row {
+ fun getValue(column: String, type: KClass): T?
}
-inline operator fun Row.get(column: String): T? = getValue(column, T::class)
-operator fun Row.get(column: ColumnHeader): T? = getValue(column.name, column.type)
\ No newline at end of file
+inline operator fun Row.get(column: String): T? = getValue(column, T::class)
+operator fun Row.get(column: ColumnHeader): T? = getValue(column.name, column.type)
\ No newline at end of file
diff --git a/dataforge-tables/src/commonMain/kotlin/hep/dataforge/tables/io/TextRows.kt b/dataforge-tables/src/commonMain/kotlin/hep/dataforge/tables/io/TextRows.kt
index f4e73702..2675b483 100644
--- a/dataforge-tables/src/commonMain/kotlin/hep/dataforge/tables/io/TextRows.kt
+++ b/dataforge-tables/src/commonMain/kotlin/hep/dataforge/tables/io/TextRows.kt
@@ -1,38 +1,56 @@
package hep.dataforge.tables.io
-import hep.dataforge.meta.get
-import hep.dataforge.meta.int
-import hep.dataforge.meta.string
import hep.dataforge.tables.*
import hep.dataforge.values.*
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.flow
+import kotlinx.coroutines.flow.toList
import kotlinx.io.Binary
import kotlinx.io.ExperimentalIoApi
import kotlinx.io.Output
import kotlinx.io.RandomAccessBinary
import kotlinx.io.text.forEachUtf8Line
import kotlinx.io.text.readUtf8Line
+import kotlinx.io.text.readUtf8StringUntilDelimiter
import kotlinx.io.text.writeUtf8String
import kotlin.reflect.KClass
-private fun readLine(header: List>, line: String): Row {
- val values = line.split("\\s+".toRegex()).map { it.parseValue() }
+/**
+ * Read a lin as a fixed width [Row]
+ */
+private fun readLine(header: ValueTableHeader, line: String): Row {
+ val values = line.trim().split("\\s+".toRegex()).map { it.lazyParseValue() }
if (values.size == header.size) {
val map = header.map { it.name }.zip(values).toMap()
return MapRow(map)
} else {
- error("Can't read line $line. Expected ${header.size} values in a line, but found ${values.size}")
+ error("Can't read line \"$line\". Expected ${header.size} values in a line, but found ${values.size}")
}
}
-
+/**
+ * Finite or infinite [Rows] created from a fixed width text binary
+ */
@ExperimentalIoApi
-class TextRows(override val header: List>, val binary: Binary) : Rows {
+class TextRows(override val header: ValueTableHeader, val binary: Binary) : Rows {
- override fun rowFlow(): Flow = binary.read {
+ /**
+ * A flow of indexes of string start offsets ignoring empty strings
+ */
+ fun indexFlow(): Flow = binary.read {
+ var counter: Int = 0
+ flow {
+ val string = readUtf8StringUntilDelimiter('\n')
+ counter += string.length
+ if (!string.isBlank()) {
+ emit(counter)
+ }
+ }
+ }
+
+ override fun rowFlow(): Flow> = binary.read {
flow {
forEachUtf8Line { line ->
if (line.isNotBlank()) {
@@ -42,35 +60,57 @@ class TextRows(override val header: List>, val binary: Binar
}
}
}
+
+ companion object
}
+/**
+ * Create a row offset index for [TextRows]
+ */
+@ExperimentalIoApi
+suspend fun TextRows.buildRowIndex(): List = indexFlow().toList()
+
+/**
+ * Finite table created from [RandomAccessBinary] with fixed width text table
+ */
@ExperimentalIoApi
class TextTable(
- override val header: List>,
+ override val header: ValueTableHeader,
val binary: RandomAccessBinary,
val index: List
) : Table {
override val columns: Collection> get() = header.map { RowTableColumn(this, it) }
- override val rows: List get() = index.map { readAt(it) }
+ override val rows: List> get() = index.map { readAt(it) }
- override fun rowFlow(): Flow = TextRows(header, binary).rowFlow()
+ override fun rowFlow(): Flow> = TextRows(header, binary).rowFlow()
- private fun readAt(offset: Int): Row {
+ private fun readAt(offset: Int): Row {
return binary.read(offset) {
val line = readUtf8Line()
return@read readLine(header, line)
}
}
- override fun getValue(row: Int, column: String, type: KClass): T? {
+ override fun getValue(row: Int, column: String, type: KClass): T? {
val offset = index[row]
return type.cast(readAt(offset)[column])
}
+
+ companion object {
+ suspend operator fun invoke(header: ValueTableHeader, binary: RandomAccessBinary): TextTable {
+ val index = TextRows(header, binary).buildRowIndex()
+ return TextTable(header, binary, index)
+ }
+ }
}
-fun Output.writeValue(value: Value, width: Int, left: Boolean = true) {
+
+/**
+ * Write a fixed width value to the output
+ */
+private fun Output.writeValue(value: Value, width: Int, left: Boolean = true) {
require(width > 5) { "Width could not be less than 5" }
val str: String = when (value.type) {
ValueType.NUMBER -> value.number.toString() //TODO apply decimal format
@@ -90,31 +130,20 @@ fun Output.writeValue(value: Value, width: Int, left: Boolean = true) {
writeUtf8String(padded)
}
-val ColumnHeader.valueType: ValueType? get() = meta["valueType"].string?.let { ValueType.valueOf(it) }
-
-private val ColumnHeader.width: Int
- get() = meta["columnWidth"].int ?: when (valueType) {
- ValueType.NUMBER -> 8
- ValueType.STRING -> 16
- ValueType.BOOLEAN -> 5
- ValueType.NULL -> 5
- null -> 16
- }
-
-
/**
* Write rows without header to the output
*/
-suspend fun Output.writeRows(rows: Rows) {
+suspend fun Output.writeRows(rows: Rows) {
@Suppress("UNCHECKED_CAST") val header = rows.header.map {
if (it.type != Value::class) error("Expected Value column, but found ${it.type}") else (it as ColumnHeader)
}
val widths: List = header.map {
- it.width
+ it.textWidth
}
rows.rowFlow().collect { row ->
header.forEachIndexed { index, columnHeader ->
writeValue(row[columnHeader] ?: Null, widths[index])
}
+ writeUtf8String("\r\n")
}
}
\ No newline at end of file
diff --git a/dataforge-tables/src/commonMain/kotlin/hep/dataforge/tables/io/textTableEnvelope.kt b/dataforge-tables/src/commonMain/kotlin/hep/dataforge/tables/io/textTableEnvelope.kt
new file mode 100644
index 00000000..29ce27b2
--- /dev/null
+++ b/dataforge-tables/src/commonMain/kotlin/hep/dataforge/tables/io/textTableEnvelope.kt
@@ -0,0 +1,42 @@
+package hep.dataforge.tables.io
+
+import hep.dataforge.io.Envelope
+import hep.dataforge.meta.*
+import hep.dataforge.tables.SimpleColumnHeader
+import hep.dataforge.tables.Table
+import hep.dataforge.values.Value
+import kotlinx.io.ByteArrayOutput
+import kotlinx.io.EmptyBinary
+import kotlinx.io.ExperimentalIoApi
+import kotlinx.io.asBinary
+
+
+@ExperimentalIoApi
+suspend fun Table.wrap(): Envelope = Envelope {
+ meta {
+ header.forEachIndexed { index, columnHeader ->
+ set("column", index.toString(), buildMeta {
+ "name" put columnHeader.name
+ if (!columnHeader.meta.isEmpty()) {
+ "meta" put columnHeader.meta
+ }
+ })
+ }
+ }
+
+ type = "table.value"
+ dataID = "valueTable[${this@wrap.hashCode()}]"
+
+ data = ByteArrayOutput().apply { writeRows(this@wrap) }.toByteArray().asBinary()
+}
+
+@DFExperimental
+@ExperimentalIoApi
+fun TextRows.Companion.readEnvelope(envelope: Envelope): TextRows {
+ val header = envelope.meta.getIndexed("column")
+ .entries.sortedBy { it.key.toInt() }
+ .map { (_, item) ->
+ SimpleColumnHeader(item.node["name"].string!!, Value::class, item.node["meta"].node ?: Meta.EMPTY)
+ }
+ return TextRows(header, envelope.data ?: EmptyBinary)
+}
\ No newline at end of file
diff --git a/dataforge-tables/src/commonTest/kotlin/hep/dataforge/tables/io/TextRowsTest.kt b/dataforge-tables/src/commonTest/kotlin/hep/dataforge/tables/io/TextRowsTest.kt
deleted file mode 100644
index 010f903f..00000000
--- a/dataforge-tables/src/commonTest/kotlin/hep/dataforge/tables/io/TextRowsTest.kt
+++ /dev/null
@@ -1,6 +0,0 @@
-package hep.dataforge.tables.io
-
-
-class TextRowsTest{
-
-}
\ No newline at end of file
diff --git a/dataforge-tables/src/jvmMain/kotlin/hep/dataforge/tables/CastColumn.kt b/dataforge-tables/src/jvmMain/kotlin/hep/dataforge/tables/CastColumn.kt
index 1ac7e138..a0bcb75e 100644
--- a/dataforge-tables/src/jvmMain/kotlin/hep/dataforge/tables/CastColumn.kt
+++ b/dataforge-tables/src/jvmMain/kotlin/hep/dataforge/tables/CastColumn.kt
@@ -8,7 +8,7 @@ import kotlin.reflect.full.cast
import kotlin.reflect.full.isSubclassOf
@Suppress("UNCHECKED_CAST")
-fun Column<*>.cast(type: KClass): Column {
+fun Column<*>.cast(type: KClass): Column {
return if (type.isSubclassOf(this.type)) {
this as Column
} else {
@@ -16,7 +16,7 @@ fun Column<*>.cast(type: KClass): Column {
}
}
-class CastColumn(val origin: Column<*>, override val type: KClass) : Column {
+class CastColumn(val origin: Column<*>, override val type: KClass) : Column {
override val name: String get() = origin.name
override val meta: Meta get() = origin.meta
override val size: Int get() = origin.size
@@ -32,5 +32,5 @@ class ColumnProperty(val table: Table, val type: KClass) :
}
}
-operator fun Collection>.get(header: ColumnHeader): Column? =
+operator fun Collection>.get(header: ColumnHeader): Column? =
find { it.name == header.name }?.cast(header.type)
diff --git a/dataforge-tables/src/jvmTest/kotlin/hep/dataforge/tables/io/TextRowsTest.kt b/dataforge-tables/src/jvmTest/kotlin/hep/dataforge/tables/io/TextRowsTest.kt
new file mode 100644
index 00000000..02d97caf
--- /dev/null
+++ b/dataforge-tables/src/jvmTest/kotlin/hep/dataforge/tables/io/TextRowsTest.kt
@@ -0,0 +1,39 @@
+package hep.dataforge.tables.io
+
+import hep.dataforge.meta.DFExperimental
+import hep.dataforge.tables.Table
+import hep.dataforge.tables.get
+import hep.dataforge.tables.row
+import hep.dataforge.values.Value
+import hep.dataforge.values.int
+import kotlinx.coroutines.flow.toList
+import kotlinx.coroutines.runBlocking
+import kotlinx.io.ExperimentalIoApi
+import kotlinx.io.toByteArray
+import kotlin.test.Test
+import kotlin.test.assertEquals
+
+
+@DFExperimental
+@ExperimentalIoApi
+class TextRowsTest {
+ val table = Table {
+ val a = column("a")
+ val b = column("b")
+ row(a to 1, b to "b1")
+ row(a to 2, b to "b2")
+ }
+
+ @Test
+ fun testTableWriteRead() {
+ runBlocking {
+ val envelope = table.wrap()
+ val string = envelope.data!!.toByteArray().decodeToString()
+ println(string)
+ val table = TextRows.readEnvelope(envelope)
+ val rows = table.rowFlow().toList()
+ assertEquals(1, rows[0]["a"]?.int)
+ assertEquals("b2", rows[1]["b"]?.string)
+ }
+ }
+}
\ No newline at end of file