Skip to content

Commit

Permalink
Adding the support for Char type (#176)
Browse files Browse the repository at this point in the history
### What's done:
- conversion from a literal string to char
- enabling some old tests
- still we need unicode conversion and fixes for literal strings
  • Loading branch information
orchestr7 authored Jan 5, 2023
1 parent a11ad25 commit d29d42c
Show file tree
Hide file tree
Showing 16 changed files with 208 additions and 112 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ package com.akuleshov7.ktoml.decoders

import com.akuleshov7.ktoml.exceptions.IllegalTypeException
import com.akuleshov7.ktoml.tree.nodes.TomlKeyValue
import com.akuleshov7.ktoml.tree.nodes.pairs.values.TomlBasicString
import com.akuleshov7.ktoml.tree.nodes.pairs.values.TomlDouble
import com.akuleshov7.ktoml.tree.nodes.pairs.values.TomlLiteralString
import com.akuleshov7.ktoml.tree.nodes.pairs.values.TomlLong
import com.akuleshov7.ktoml.utils.FloatingPointLimitsEnum
import com.akuleshov7.ktoml.utils.FloatingPointLimitsEnum.*
Expand Down Expand Up @@ -32,7 +34,35 @@ public abstract class TomlAbstractDecoder : AbstractDecoder() {
override fun decodeShort(): Short = decodePrimitiveType()
override fun decodeInt(): Int = decodePrimitiveType()
override fun decodeFloat(): Float = decodePrimitiveType()
override fun decodeChar(): Char = invalidType("Char", "String")
override fun decodeChar(): Char {
val keyValue = decodeKeyValue()
return when (val value = keyValue.value) {
// converting to Char from a parsed Long number and checking bounds for the Char (MIN-MAX range)
is TomlLong -> validateAndConvertInteger(value.content as Long, keyValue.lineNo, CHAR) { Char(it.toInt()) }
// converting to Char from a parsed Literal String (with single quotes: '')
is TomlLiteralString ->
try {
(value.content as String).single()
} catch (ex: NoSuchElementException) {
throw IllegalTypeException("Empty value is not allowed for type [Char], " +
"please check the value: [${value.content}] or use [String] type for deserialization of " +
"[${keyValue.key}] instead", keyValue.lineNo)
} catch (ex: IllegalArgumentException) {
throw IllegalTypeException("[Char] type should be used for decoding of single character, but " +
"received multiple characters instead: [${value.content}]. " +
"If you really want to decode multiple chars, use [String] instead.", keyValue.lineNo)
}
// to avoid confusion, we prohibit basic strings with double quotes for decoding to a Char type
is TomlBasicString -> throw IllegalTypeException("Double quotes were used in the input for deserialization " +
"of [Char]. Use [String] type or single quotes ('') instead for: [${value.content}]", keyValue.lineNo)
// all other toml tree types are not supported
else -> throw IllegalTypeException(
"Cannot decode the key [${keyValue.key.last()}] with the value [${keyValue.value.content}]" +
" and with the provided type [Char]. Please check the type in your Serializable class or it's nullability",
keyValue.lineNo
)
}
}

// Valid Toml types that should be properly decoded
override fun decodeBoolean(): Boolean = decodePrimitiveType()
Expand Down Expand Up @@ -78,7 +108,7 @@ public abstract class TomlAbstractDecoder : AbstractDecoder() {
} catch (e: ClassCastException) {
throw IllegalTypeException(
"Cannot decode the key [${keyValue.key.last()}] with the value [${keyValue.value.content}]" +
" with the provided type [${T::class}]. Please check the type in your Serializable class or it's nullability",
" and with the provided type [${T::class}]. Please check the type in your Serializable class or it's nullability",
keyValue.lineNo
)
}
Expand Down Expand Up @@ -118,10 +148,10 @@ public abstract class TomlAbstractDecoder : AbstractDecoder() {
*/
private inline fun <reified T> decodeInteger(content: Long, lineNo: Int): T =
when (T::class) {
Byte::class -> validateAndConvertInteger(content, lineNo, BYTE) { num: Long -> num.toByte() as T }
Short::class -> validateAndConvertInteger(content, lineNo, SHORT) { num: Long -> num.toShort() as T }
Int::class -> validateAndConvertInteger(content, lineNo, INT) { num: Long -> num.toInt() as T }
Long::class -> validateAndConvertInteger(content, lineNo, LONG) { num: Long -> num as T }
Byte::class -> validateAndConvertInteger(content, lineNo, BYTE) { it.toByte() as T }
Short::class -> validateAndConvertInteger(content, lineNo, SHORT) { it.toShort() as T }
Int::class -> validateAndConvertInteger(content, lineNo, INT) { it.toInt() as T }
Long::class -> validateAndConvertInteger(content, lineNo, LONG) { it as T }
Double::class, Float::class -> throw IllegalTypeException(
"Expected floating-point number, but received integer literal: <$content>. " +
"Deserialized floating-point number should have a dot: <$content.0>",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ public class TomlMainDecoder(
private fun getCurrentNode() = rootNode.getNeighbourNodes().elementAt(elementIndex - 1)

/**
* Trying to decode the value (ite
* Trying to decode the value using elementIndex
* |--- child1, child2, ... , childN
* ------------elementIndex------->
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@ private fun String.validateSymbols(lineNo: Int) {
throw ParseException(
"Not able to parse the key: [$this] as it contains invalid symbols." +
" In case you would like to use special symbols - use quotes as" +
" it is required by TOML standard: \"My key ~ with special % symbols\"",
" it is required by TOML standard: \"My key with special (%, ±) symbols\" = \"value\"",
lineNo
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ package com.akuleshov7.ktoml.utils
*/
public enum class IntegerLimitsEnum(public val min: Long, public val max: Long) {
BYTE(Byte.MIN_VALUE.toLong(), Byte.MAX_VALUE.toLong()),
CHAR(Char.MIN_VALUE.code.toLong(), Char.MAX_VALUE.code.toLong()),
INT(Int.MIN_VALUE.toLong(), Int.MAX_VALUE.toLong()),
LONG(Long.MIN_VALUE, Long.MAX_VALUE),
SHORT(Short.MIN_VALUE.toLong(), Short.MAX_VALUE.toLong()),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -356,10 +356,10 @@ class SimpleArrayDecoderTest {
@Test
fun testSimpleArrayDecoderInNestedTable() {
var test = """
|[table]
|name = "my name"
|configurationList = ["a", "b", "c"]
""".trimMargin()
[table]
name = "my name"
configurationList = ["a", "b", "c"]
"""

assertEquals(
ArrayInInlineTable(
Expand All @@ -369,10 +369,10 @@ class SimpleArrayDecoderTest {

test =
"""
|[table]
|configurationList = ["a", "b", "c"]
|name = "my name"
""".trimMargin()
[table]
configurationList = ["a", "b", "c"]
name = "my name"
"""

assertEquals(
ArrayInInlineTable(
Expand All @@ -381,11 +381,11 @@ class SimpleArrayDecoderTest {
)

val testTable = """
|configurationList1 = ["a", "b", "c"]
|configurationList2 = ["a", "b", "c"]
|[table]
|name = "my name"
""".trimMargin()
configurationList1 = ["a", "b", "c"]
configurationList2 = ["a", "b", "c"]
[table]
name = "my name"
"""

assertEquals(
TestArrays(
Expand All @@ -396,13 +396,13 @@ class SimpleArrayDecoderTest {
)

val testTableAndVariables = """
|name1 = "simple"
|configurationList1 = ["a", "b", "c"]
|name2 = "simple"
|configurationList2 = ["a", "b", "c"]
|[table]
|name = "my name"
""".trimMargin()
name1 = "simple"
configurationList1 = ["a", "b", "c"]
name2 = "simple"
configurationList2 = ["a", "b", "c"]
[table]
name = "my name"
"""


assertEquals(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,8 @@
package com.akuleshov7.ktoml.decoders

import com.akuleshov7.ktoml.Toml
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import kotlin.test.Ignore
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertFailsWith

@Serializable
data class TomlArrayOfTables(val a: List<Long>)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package com.akuleshov7.ktoml.decoders

import com.akuleshov7.ktoml.Toml
import kotlinx.serialization.Serializable
import kotlinx.serialization.decodeFromString
import kotlin.test.Ignore
import kotlin.test.Test
import kotlin.test.assertEquals


class CharDecoderTest {
@Serializable
data class MyClass(
val a: Char,
val b: Char,
val c: Char,
)

@Test
fun charBasicTest() {
val test =
"""
a = 123
b = '4'
c = 'D'
"""

// FixMe #177: actually this logic is invalid, because Literal Strings should not be making a conversion of str
val decoded = Toml.decodeFromString<MyClass>(test)
assertEquals(decoded, MyClass('{', '4', 'D'))
}

@Test
@Ignore
fun charUnicodeSymbolsTest() {
val test =
"""
a = '\u0048'
b = '\u0FCA'
c = '\u0002'
"""

val decoded = Toml.decodeFromString<MyClass>(test)
assertEquals(decoded, MyClass('{', '\n', '\t'))
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ class CustomSerializerTest {
""".trimIndent()
)
)
UInt.MAX_VALUE
}

@Serializable
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -110,16 +110,16 @@ class DottedKeysDecoderTest {
),
Toml().decodeFromString(
"""
|table2.b.d = 2
|[table1]
|a.c = 1
|[table1.table2]
|b.b.a = 1
|[table2]
|b.f = 2
|table2."foo bar".d = 2
|[table3]
""".trimMargin()
table2.b.d = 2
[table1]
a.c = 1
[table1.table2]
b.b.a = 1
[table2]
b.f = 2
table2."foo bar".d = 2
[table3]
"""
)
)
}
Expand All @@ -130,11 +130,11 @@ class DottedKeysDecoderTest {
SimpleNestedExample(table2 = Table4(b = B(f = 2, d = 2), e = 5)),
Toml(TomlInputConfig(true)).decodeFromString(
"""
|table2.b.d = 2
|[table2]
|e = 5
|b.f = 2
""".trimMargin()
table2.b.d = 2
[table2]
e = 5
b.f = 2
"""
)
)
}
Expand All @@ -145,13 +145,13 @@ class DottedKeysDecoderTest {
SimpleNestedExample(table2 = Table4(b = B(f = 7, d = 2), e = 6)),
Toml(TomlInputConfig(true)).decodeFromString(
"""
|[table2]
|table2."foo bar".d = 2
|e = 6
|[table2.b]
|d = 2
|f = 7
""".trimMargin()
[table2]
table2."foo bar".d = 2
e = 6
[table2.b]
d = 2
f = 7
"""
)
)
}
Expand Down Expand Up @@ -208,14 +208,14 @@ class DottedKeysDecoderTest {
),
Toml.decodeFromString(
"""
|[a."b.c..".d."e.f"]
| val = 1
| [a]
| [a."b.c.."]
| val = 2
| [a."b.c..".inner]
| val = 3
""".trimMargin()
[a."b.c..".d."e.f"]
val = 1
[a]
[a."b.c.."]
val = 2
[a."b.c..".inner]
val = 3
"""
)
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -279,11 +279,11 @@ class GeneralDecoderTest {
@Test
fun testChildTableBeforeParent() {
val test = """
|[a.b]
| c = 5
| [a]
| a = true
""".trimMargin()
[a.b]
c = 5
[a]
a = true
"""
TomlInputConfig(true)

assertEquals(ChildTableBeforeParent(A(B(5), true)), Toml.decodeFromString(test))
Expand Down Expand Up @@ -377,20 +377,20 @@ class GeneralDecoderTest {
@Test
fun severalTablesOnTheSameLevel() {
val test = """
|[table]
|[table.in1]
| a = 1
| [table.in1.in1]
| a = 1
| [table.in1.in2]
| a = 1
|[table.in2]
| a = 1
| [table.in2.in1]
| a = 1
| [table.in2.in2]
| a = 1
""".trimMargin()
[table]
[table.in1]
a = 1
[table.in1.in1]
a = 1
[table.in1.in2]
a = 1
[table.in2]
a = 1
[table.in2.in1]
a = 1
[table.in2.in2]
a = 1
"""

assertEquals(
MyTest(
Expand Down
Loading

0 comments on commit d29d42c

Please sign in to comment.