Skip to content

Commit

Permalink
Merge pull request #238 from akuleshov7/support-value-class
Browse files Browse the repository at this point in the history
Add encoding value classes
  • Loading branch information
BOOMeranGG authored Sep 1, 2023
2 parents 873ce99 + 7eadee5 commit 8db0064
Show file tree
Hide file tree
Showing 2 changed files with 182 additions and 9 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.SerializationStrategy
import kotlinx.serialization.descriptors.*
import kotlinx.serialization.encoding.AbstractEncoder
import kotlinx.serialization.encoding.Encoder
import kotlinx.serialization.modules.SerializersModule

/**
Expand Down Expand Up @@ -104,6 +105,12 @@ public abstract class TomlAbstractEncoder protected constructor(
appendValue(TomlNull())
}

override fun encodeInline(descriptor: SerialDescriptor): Encoder {
// Value (inline) class always has one element
encodeElement(descriptor, 0)
return this
}

override fun encodeString(value: String) {
if (!encodeAsKey(value)) {
appendValue(
Expand All @@ -116,6 +123,7 @@ public abstract class TomlAbstractEncoder protected constructor(
}
}

@Suppress("NESTED_BLOCK")
override fun <T> encodeSerializableValue(serializer: SerializationStrategy<T>, value: T) {
when (val desc = serializer.descriptor) {
instantDescriptor,
Expand All @@ -126,14 +134,14 @@ public abstract class TomlAbstractEncoder protected constructor(
}
else -> when (val kind = desc.kind) {
is StructureKind,
is PolymorphicKind -> if (!encodeAsKey(value as Any, desc.serialName)) {
val encoder = encodeStructure(kind)

serializer.serialize(encoder, value)

elementIndex = encoder.elementIndex

attributes.reset()
is PolymorphicKind -> when {
desc.isInline -> serializer.serialize(this, value)
!encodeAsKey(value as Any, desc.serialName) -> {
val encoder = encodeStructure(kind)
serializer.serialize(encoder, value)
elementIndex = encoder.elementIndex
attributes.reset()
}
}
else -> super.encodeSerializableValue(serializer, value)
}
Expand Down Expand Up @@ -204,7 +212,13 @@ public abstract class TomlAbstractEncoder protected constructor(

protected open fun isNextElementKey(descriptor: SerialDescriptor, index: Int): Boolean {
when (val kind = descriptor.kind) {
StructureKind.CLASS -> setKey(descriptor.getElementName(index))
StructureKind.CLASS -> {
// We should keep previous key when we have value (inline) class
// But if key is null, it means that value class isn't nested, and we have to use its own key
if (!descriptor.isInline || attributes.key == null) {
setKey(descriptor.getElementName(index))
}
}
StructureKind.MAP -> {
// When the index is even (key) mark the next element as a key and
// skip annotations and element index incrementing.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
package com.akuleshov7.ktoml.encoders

import com.akuleshov7.ktoml.Toml
import kotlinx.serialization.Serializable
import kotlinx.serialization.encodeToString
import kotlin.jvm.JvmInline
import kotlin.test.Test
import kotlin.test.assertEquals

class ValueClassEncoderTest {

@Serializable
@JvmInline
value class Color(val rgb: Int)

@Serializable
data class NamedColor(val color: Color, val name: String)

@Test
fun testForSimpleValueClass() {
val color = Color(15)
val result = Toml.encodeToString(color)

assertEquals("rgb = 15", result)
}

@Test
fun testForNestedValueClass() {
val namedColor = NamedColor(
Color(150),
"black"
)

val result = Toml.encodeToString(namedColor)
assertEquals(
"""
color = 150
name = "black"
""".trimIndent(),
result
)
}

@Test
fun testForLisOfValueClass() {
@Serializable
class Palette(val colors: List<Color>)
val palette = Palette(
listOf(
Color(0),
Color(255),
Color(128),
)
)

val result = Toml.encodeToString(palette)
assertEquals("colors = [ 0, 255, 128 ]", result)
}

@Serializable
@JvmInline
value class Num(val int: Int)

@Serializable
data class Nums(
val num1: Num,
val num2: Num,
)

@Test
fun testForMultipleValueClass() {
val nums = Nums(
num1 = Num(5),
num2 = Num(111)
)
val result = Toml.encodeToString(nums)

assertEquals(
"""
num1 = 5
num2 = 111
""".trimIndent(),
result
)
}

@Serializable
data class MyObject(
val height: Int,
val width: Int
)

@Serializable
@JvmInline
value class Info(val obj: MyObject)

@Serializable
data class InfoWrapper(
val metaInfo1: Int,
val info: Info,
val metaInfo2: String
)

@Test
fun testForValueClassWithObjectInside() {
val obj = InfoWrapper(
metaInfo1 = 1,
info = Info(MyObject(10, 20)),
metaInfo2 = "test"
)
val result = Toml.encodeToString(obj)

assertEquals(
"""
metaInfo1 = 1
metaInfo2 = "test"
[info]
height = 10
width = 20
""".trimIndent(),
result
)
}

@Serializable
@JvmInline
value class AnotherInfoWrapper(val info: Info)

@Test
fun testForValueClassInsideValueClass() {
val obj = AnotherInfoWrapper(Info(MyObject(10, 20)))
val result = Toml.encodeToString(obj)

assertEquals(
"""
[info]
height = 10
width = 20
""".trimIndent(),
result
)
}

@Test
fun testDataClassInsideValueClass() {
val obj = Info(MyObject(32, 64))
val result = Toml.encodeToString(obj)

assertEquals(
"""
[obj]
height = 32
width = 64
""".trimIndent(),
result
)
}
}

0 comments on commit 8db0064

Please sign in to comment.