Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add encoding value classes #238

Merged
merged 2 commits into from
Sep 1, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@ -126,7 +133,9 @@ public abstract class TomlAbstractEncoder protected constructor(
}
else -> when (val kind = desc.kind) {
is StructureKind,
is PolymorphicKind -> if (!encodeAsKey(value as Any, desc.serialName)) {
is PolymorphicKind -> if (desc.isInline) {
BOOMeranGG marked this conversation as resolved.
Show resolved Hide resolved
serializer.serialize(this, value)
} else if (!encodeAsKey(value as Any, desc.serialName)) {
val encoder = encodeStructure(kind)

serializer.serialize(encoder, value)
Expand Down Expand Up @@ -204,7 +213,15 @@ 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) {
// do nothing
BOOMeranGG marked this conversation as resolved.
Show resolved Hide resolved
} else {
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)
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, even nested value classes work, great!


@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
)
}
}