Skip to content

Commit 9ab4f22

Browse files
authored
Support array of inline tables encoding (#350)
* Support array of inline tables encoding
1 parent 238f7a9 commit 9ab4f22

File tree

8 files changed

+261
-107
lines changed

8 files changed

+261
-107
lines changed

ktoml-core/src/commonMain/kotlin/com/akuleshov7/ktoml/annotations/TomlInlineTable.kt

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
package com.akuleshov7.ktoml.annotations
22

3-
import kotlin.annotation.AnnotationTarget.*
43
import kotlinx.serialization.ExperimentalSerializationApi
54
import kotlinx.serialization.SerialInfo
65

@@ -12,7 +11,8 @@ import kotlinx.serialization.SerialInfo
1211
* data class Data(
1312
* @TomlInlineTable
1413
* val inlineTable: Table,
15-
* val tableArray: List<@TomlInlineTable Table>,
14+
* @TomlInlineTable
15+
* val tableArray: List<Table>,
1616
* val inlineTable2: Table2
1717
* )
1818
*
@@ -44,9 +44,7 @@ import kotlinx.serialization.SerialInfo
4444
@OptIn(ExperimentalSerializationApi::class)
4545
@SerialInfo
4646
@Target(
47-
PROPERTY,
48-
TYPE_PARAMETER,
49-
CLASS,
50-
TYPE
47+
AnnotationTarget.PROPERTY,
48+
AnnotationTarget.CLASS,
5149
)
5250
public annotation class TomlInlineTable

ktoml-core/src/commonMain/kotlin/com/akuleshov7/ktoml/encoders/TomlArrayEncoder.kt

Lines changed: 35 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,11 @@ package com.akuleshov7.ktoml.encoders
22

33
import com.akuleshov7.ktoml.TomlOutputConfig
44
import com.akuleshov7.ktoml.exceptions.InternalEncodingException
5-
import com.akuleshov7.ktoml.exceptions.UnsupportedEncodingFeatureException
65
import com.akuleshov7.ktoml.tree.nodes.*
76
import com.akuleshov7.ktoml.tree.nodes.pairs.keys.TomlKey
87
import com.akuleshov7.ktoml.tree.nodes.pairs.values.TomlArray
98
import com.akuleshov7.ktoml.tree.nodes.pairs.values.TomlValue
9+
import com.akuleshov7.ktoml.tree.nodes.tables.InlineTableType
1010
import kotlinx.serialization.ExperimentalSerializationApi
1111
import kotlinx.serialization.descriptors.PolymorphicKind
1212
import kotlinx.serialization.descriptors.SerialDescriptor
@@ -95,10 +95,16 @@ public class TomlArrayEncoder internal constructor(
9595
is PolymorphicKind ->
9696
// Nested primitive array
9797
arrayEncoder(rootNode, attributes)
98-
else ->
99-
throw UnsupportedEncodingFeatureException(
100-
"Inline tables are not yet supported as array elements."
98+
else -> {
99+
val element = TomlArrayOfTablesElement(
100+
elementIndex,
101+
attributes.comments,
102+
attributes.inlineComment
101103
)
104+
105+
tables += element
106+
inlineTableEncoder(element)
107+
}
102108
}
103109
} else {
104110
val element = TomlArrayOfTablesElement(
@@ -119,7 +125,7 @@ public class TomlArrayEncoder internal constructor(
119125
}
120126

121127
override fun endStructure(descriptor: SerialDescriptor) {
122-
if (attributes.isInline) {
128+
if (attributes.isInline && tables.isEmpty()) {
123129
val array = TomlArray(values, attributes.isMultiline)
124130

125131
parent?.let {
@@ -163,14 +169,31 @@ public class TomlArrayEncoder internal constructor(
163169
}
164170
}
165171

166-
val tableArray = TomlTable(
167-
TomlKey(attributes.parent!!.getFullKey(), elementIndex),
168-
elementIndex,
169-
type = TableType.ARRAY,
170-
isSynthetic = isSynthetic
171-
)
172+
appendParentalTable(isSynthetic)
173+
}
172174

173-
tables.forEach(tableArray::appendChild)
175+
private fun appendParentalTable(isSynthetic: Boolean) {
176+
val allChildren = tables.flatMap { it.children }
177+
val tableArray = if (allChildren.any { it is TomlInlineTable && it.key == null }) {
178+
TomlInlineTable(
179+
key = TomlKey(attributes.parent!!.key!!, elementIndex),
180+
tomlKeyValues = tables,
181+
inlineTableType = InlineTableType.ARRAY,
182+
lineNo = elementIndex,
183+
comments = emptyList(),
184+
inlineComment = "",
185+
multiline = attributes.isMultiline,
186+
)
187+
} else {
188+
TomlTable(
189+
TomlKey(attributes.parent!!.getFullKey(), elementIndex),
190+
elementIndex,
191+
type = TableType.ARRAY,
192+
isSynthetic = isSynthetic
193+
).apply {
194+
tables.forEach(this::appendChild)
195+
}
196+
}
174197

175198
rootNode.appendChild(tableArray)
176199
}

ktoml-core/src/commonMain/kotlin/com/akuleshov7/ktoml/encoders/TomlInlineTableEncoder.kt

Lines changed: 24 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,11 @@ package com.akuleshov7.ktoml.encoders
22

33
import com.akuleshov7.ktoml.TomlOutputConfig
44
import com.akuleshov7.ktoml.exceptions.InternalEncodingException
5-
import com.akuleshov7.ktoml.tree.nodes.TomlInlineTable
6-
import com.akuleshov7.ktoml.tree.nodes.TomlKeyValueArray
7-
import com.akuleshov7.ktoml.tree.nodes.TomlKeyValuePrimitive
8-
import com.akuleshov7.ktoml.tree.nodes.TomlNode
5+
import com.akuleshov7.ktoml.tree.nodes.*
96
import com.akuleshov7.ktoml.tree.nodes.pairs.keys.TomlKey
107
import com.akuleshov7.ktoml.tree.nodes.pairs.values.TomlArray
118
import com.akuleshov7.ktoml.tree.nodes.pairs.values.TomlValue
9+
import com.akuleshov7.ktoml.tree.nodes.tables.InlineTableType
1210
import kotlinx.serialization.ExperimentalSerializationApi
1311
import kotlinx.serialization.descriptors.SerialDescriptor
1412
import kotlinx.serialization.descriptors.SerialKind
@@ -102,21 +100,25 @@ public class TomlInlineTableEncoder internal constructor(
102100

103101
override fun beginStructure(descriptor: SerialDescriptor): CompositeEncoder {
104102
val (_, _, _, _, _, _, comments, inlineComment) = attributes
105-
val name = attributes.keyOrThrow()
103+
104+
val tomlKey = if (parent !is TomlArrayEncoder) {
105+
TomlKey(attributes.keyOrThrow(), elementIndex)
106+
} else {
107+
null
108+
}
106109

107110
val inlineTable = TomlInlineTable(
108-
TomlKey(name, elementIndex),
109-
pairs,
110-
elementIndex,
111-
comments,
112-
inlineComment
111+
key = tomlKey,
112+
tomlKeyValues = pairs,
113+
inlineTableType = InlineTableType.PRIMITIVE,
114+
lineNo = elementIndex,
115+
comments = comments,
116+
inlineComment = inlineComment,
113117
)
114118

115119
when (parent) {
116120
is TomlInlineTableEncoder -> parent.pairs += inlineTable
117-
is TomlArrayEncoder -> {
118-
// Todo: Implement this when inline table arrays are supported.
119-
}
121+
is TomlArrayEncoder -> rootNode.appendChild(inlineTable)
120122
else -> rootNode.appendChild(inlineTable)
121123
}
122124

@@ -143,21 +145,23 @@ public class TomlInlineTableEncoder internal constructor(
143145
child.value,
144146
child.lineNo,
145147
child.comments,
146-
child.inlineComment
148+
child.inlineComment,
147149
)
148150
is TomlKeyValueArray -> TomlKeyValueArray(
149151
TomlKey(name, child.lineNo),
150152
child.value,
151153
child.lineNo,
152154
child.comments,
153-
child.inlineComment
155+
child.inlineComment,
154156
)
155157
is TomlInlineTable -> TomlInlineTable(
156-
TomlKey(name, elementIndex),
157-
child.tomlKeyValues,
158-
child.lineNo,
159-
child.comments,
160-
child.inlineComment
158+
key = TomlKey(name, elementIndex),
159+
tomlKeyValues = child.tomlKeyValues,
160+
inlineTableType = InlineTableType.PRIMITIVE,
161+
lineNo = child.lineNo,
162+
comments = child.comments,
163+
inlineComment = child.inlineComment,
164+
multiline = child.multiline,
161165
)
162166
else -> throw InternalEncodingException("Tried to encode $child as a pair, but it has unknown type.")
163167
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package com.akuleshov7.ktoml.tree.nodes.tables
2+
3+
/**
4+
* Type of inline table: primitive = "table = { a = 5, b = 6 }" or array: "table_arr = [ { a = 5 }, { a = 6 } ]"
5+
*
6+
*/
7+
public enum class InlineTableType {
8+
ARRAY,
9+
PRIMITIVE,
10+
;
11+
}

ktoml-core/src/commonMain/kotlin/com/akuleshov7/ktoml/tree/nodes/tables/TomlInlineTable.kt

Lines changed: 53 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import com.akuleshov7.ktoml.TomlOutputConfig
55
import com.akuleshov7.ktoml.exceptions.ParseException
66
import com.akuleshov7.ktoml.parsers.*
77
import com.akuleshov7.ktoml.tree.nodes.pairs.keys.TomlKey
8+
import com.akuleshov7.ktoml.tree.nodes.tables.InlineTableType
89
import com.akuleshov7.ktoml.writers.TomlEmitter
910

1011
/**
@@ -13,12 +14,16 @@ import com.akuleshov7.ktoml.writers.TomlEmitter
1314
* @param lineNo
1415
* @param comments
1516
* @param inlineComment
17+
* @param inlineTableType type of inline table (primitive or array)
1618
* @property tomlKeyValues The key-value pairs in the inline table
17-
* @property key
19+
* @property multiline whether the inline table should be written in multiple lines
20+
* @property key null when this inline table is part of array of tables
1821
*/
1922
public class TomlInlineTable internal constructor(
20-
public val key: TomlKey,
23+
public val key: TomlKey?,
2124
internal val tomlKeyValues: List<TomlNode>,
25+
private val inlineTableType: InlineTableType,
26+
public val multiline: Boolean = false,
2227
lineNo: Int,
2328
comments: List<String> = emptyList(),
2429
inlineComment: String = ""
@@ -35,13 +40,17 @@ public class TomlInlineTable internal constructor(
3540
lineNo: Int,
3641
comments: List<String> = emptyList(),
3742
inlineComment: String = "",
38-
config: TomlInputConfig = TomlInputConfig()
43+
config: TomlInputConfig = TomlInputConfig(),
44+
inlineTableType: InlineTableType = InlineTableType.PRIMITIVE,
45+
multiline: Boolean = false,
3946
) : this(
4047
TomlKey(keyValuePair.first, lineNo),
4148
keyValuePair.second.parseInlineTableValue(keyValuePair, lineNo, config),
49+
inlineTableType,
50+
multiline,
4251
lineNo,
4352
comments,
44-
inlineComment
53+
inlineComment,
4554
)
4655

4756
public fun returnTable(tomlFileHead: TomlFile, currentParentalNode: TomlNode): TomlTable {
@@ -77,31 +86,62 @@ public class TomlInlineTable internal constructor(
7786
emitter: TomlEmitter,
7887
config: TomlOutputConfig
7988
) {
80-
key.write(emitter)
89+
key?.let {
90+
it.write(emitter)
91+
emitter.emitPairDelimiter()
92+
}
8193

82-
emitter.emitPairDelimiter()
83-
.startInlineTable()
94+
when (inlineTableType) {
95+
InlineTableType.PRIMITIVE -> emitter.startInlineTable()
96+
InlineTableType.ARRAY -> emitter.startArray()
97+
}
8498

99+
val isMultiline = multiline && inlineTableType == InlineTableType.ARRAY
100+
if (isMultiline) {
101+
emitter.indent()
102+
}
85103
tomlKeyValues.forEachIndexed { i, pair ->
86104
if (i > 0) {
87105
emitter.emitElementDelimiter()
88106
}
89-
90-
emitter.emitWhitespace()
107+
if (isMultiline) {
108+
emitter.emitNewLine()
109+
} else {
110+
emitter.emitWhitespace()
111+
}
91112

92113
pair.write(emitter, config)
93114
}
115+
if (isMultiline) {
116+
emitter.dedent()
117+
}
94118

95-
emitter.emitWhitespace()
96-
.endInlineTable()
119+
if (!isMultiline) {
120+
emitter.emitWhitespace()
121+
}
122+
123+
writeEnding(emitter, isMultiline)
124+
}
125+
126+
private fun writeEnding(emitter: TomlEmitter, isMultiline: Boolean) {
127+
when (inlineTableType) {
128+
InlineTableType.PRIMITIVE -> emitter.endInlineTable()
129+
InlineTableType.ARRAY -> {
130+
if (isMultiline) {
131+
emitter.emitNewLine()
132+
}
133+
emitter.emitIndent()
134+
.endArray()
135+
}
136+
}
97137
}
98138

99139
private fun createTableRoot(currentParentalNode: TomlNode): TomlTable = TomlTable(
100140
TomlKey(
101141
when (currentParentalNode) {
102-
is TomlTable -> currentParentalNode.fullTableKey.keyParts + key.keyParts
142+
is TomlTable -> currentParentalNode.fullTableKey.keyParts + key!!.keyParts
103143
is TomlArrayOfTablesElement -> (currentParentalNode.parent as TomlTable)
104-
.fullTableKey.keyParts + key.keyParts
144+
.fullTableKey.keyParts + key!!.keyParts
105145
else -> listOf(name)
106146
},
107147
),

ktoml-core/src/commonTest/kotlin/com/akuleshov7/ktoml/encoders/ArrayEncoderTest.kt

Lines changed: 0 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,7 @@ package com.akuleshov7.ktoml.encoders
22

33
import com.akuleshov7.ktoml.annotations.TomlInlineTable
44
import com.akuleshov7.ktoml.annotations.TomlLiteral
5-
import com.akuleshov7.ktoml.annotations.TomlMultiline
65
import kotlinx.serialization.Serializable
7-
import kotlin.test.Ignore
86
import kotlin.test.Test
97

108
class ArrayEncoderTest {
@@ -54,32 +52,6 @@ class ArrayEncoderTest {
5452
)
5553
}
5654

57-
@Test
58-
@Ignore
59-
fun inlineTableArrayTest() {
60-
@Serializable
61-
@TomlInlineTable
62-
data class InlineTable(val index: Long)
63-
64-
@Serializable
65-
data class InlineTableArray(
66-
@TomlMultiline
67-
val inlineTables: List<InlineTable> =
68-
(0L..2L).map(::InlineTable)
69-
)
70-
71-
assertEncodedEquals(
72-
value = InlineTableArray(),
73-
expectedToml = """
74-
inlineTables = [
75-
{ index = 0 },
76-
{ index = 1 },
77-
{ index = 2 }
78-
]
79-
""".trimIndent()
80-
)
81-
}
82-
8355
@Test
8456
fun nestedArrayTest() {
8557
@Serializable

0 commit comments

Comments
 (0)