Skip to content

Commit c3067ea

Browse files
authored
Adding extra information to errors and exceptions (#212)
### What's done: - added extra information to errors and exceptions - updated diktat to latest version and fixed indenting
1 parent 3e84eea commit c3067ea

25 files changed

+284
-295
lines changed

buildSrc/build.gradle.kts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ dependencies {
1010
// this hack prevents the following bug: https://github.com/gradle/gradle/issues/9770
1111
implementation("org.jetbrains.kotlin:kotlin-gradle-plugin:1.8.20")
1212

13-
implementation("org.cqfn.diktat:diktat-gradle-plugin:1.1.0")
13+
implementation("org.cqfn.diktat:diktat-gradle-plugin:1.2.5")
1414
implementation("io.gitlab.arturbosch.detekt:detekt-gradle-plugin:1.15.0")
1515
implementation("io.github.gradle-nexus:publish-plugin:1.1.0")
1616
implementation("org.ajoberstar.reckon:reckon-gradle:0.13.0")

ktoml-core/src/commonMain/kotlin/com/akuleshov7/ktoml/TomlConfig.kt

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -95,13 +95,13 @@ public data class TomlInputConfig(
9595
ignoreUnknownNames: Boolean = false,
9696
allowEmptyToml: Boolean = true
9797
): TomlInputConfig =
98-
TomlInputConfig(
99-
ignoreUnknownNames,
100-
allowEmptyValues = false,
101-
allowNullValues = false,
102-
allowEmptyToml,
103-
allowEscapedQuotesInLiteralStrings = false
104-
)
98+
TomlInputConfig(
99+
ignoreUnknownNames,
100+
allowEmptyValues = false,
101+
allowNullValues = false,
102+
allowEmptyToml,
103+
allowEscapedQuotesInLiteralStrings = false
104+
)
105105
}
106106
}
107107

@@ -135,13 +135,13 @@ public data class TomlOutputConfig(
135135
ignoreDefaultValues: Boolean = false,
136136
explicitTables: Boolean = false,
137137
): TomlOutputConfig =
138-
TomlOutputConfig(
139-
indentation,
140-
allowEscapedQuotesInLiteralStrings = false,
141-
ignoreNullValues = true,
142-
ignoreDefaultValues,
143-
explicitTables
144-
)
138+
TomlOutputConfig(
139+
indentation,
140+
allowEscapedQuotesInLiteralStrings = false,
141+
ignoreNullValues = true,
142+
ignoreDefaultValues,
143+
explicitTables
144+
)
145145
}
146146
}
147147

ktoml-core/src/commonMain/kotlin/com/akuleshov7/ktoml/decoders/TomlAbstractDecoder.kt

Lines changed: 32 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -80,21 +80,21 @@ public abstract class TomlAbstractDecoder : AbstractDecoder() {
8080
override fun decodeString(): String = decodePrimitiveType()
8181

8282
protected fun DeserializationStrategy<*>.isDateTime(): Boolean =
83-
descriptor == instantSerializer.descriptor ||
84-
descriptor == localDateTimeSerializer.descriptor ||
85-
descriptor == localDateSerializer.descriptor ||
86-
descriptor == localTimeSerializer.descriptor
83+
descriptor == instantSerializer.descriptor ||
84+
descriptor == localDateTimeSerializer.descriptor ||
85+
descriptor == localDateSerializer.descriptor ||
86+
descriptor == localTimeSerializer.descriptor
8787

8888
// Cases for date-time types
8989
@Suppress("UNCHECKED_CAST")
9090
override fun <T> decodeSerializableValue(deserializer: DeserializationStrategy<T>): T =
91-
when (deserializer.descriptor) {
92-
instantSerializer.descriptor -> decodePrimitiveType<Instant>() as T
93-
localDateTimeSerializer.descriptor -> decodePrimitiveType<LocalDateTime>() as T
94-
localDateSerializer.descriptor -> decodePrimitiveType<LocalDate>() as T
95-
localTimeSerializer.descriptor -> decodePrimitiveType<LocalTime>() as T
96-
else -> super.decodeSerializableValue(deserializer)
97-
}
91+
when (deserializer.descriptor) {
92+
instantSerializer.descriptor -> decodePrimitiveType<Instant>() as T
93+
localDateTimeSerializer.descriptor -> decodePrimitiveType<LocalDateTime>() as T
94+
localDateSerializer.descriptor -> decodePrimitiveType<LocalDate>() as T
95+
localTimeSerializer.descriptor -> decodePrimitiveType<LocalTime>() as T
96+
else -> super.decodeSerializableValue(deserializer)
97+
}
9898

9999
internal abstract fun decodeKeyValue(): TomlKeyValue
100100

@@ -124,16 +124,16 @@ public abstract class TomlAbstractDecoder : AbstractDecoder() {
124124
}
125125

126126
private inline fun <reified T> decodeFloatingPoint(content: Double, lineNo: Int): T =
127-
when (T::class) {
128-
Float::class -> validateAndConvertFloatingPoint(
129-
content,
130-
lineNo,
131-
FLOAT
132-
) { num: Double -> num.toFloat() as T }
127+
when (T::class) {
128+
Float::class -> validateAndConvertFloatingPoint(
129+
content,
130+
lineNo,
131+
FLOAT
132+
) { num: Double -> num.toFloat() as T }
133133

134-
Double::class -> validateAndConvertFloatingPoint(content, lineNo, DOUBLE) { num: Double -> num as T }
135-
else -> invalidType(T::class.toString(), "Signed Type")
136-
}
134+
Double::class -> validateAndConvertFloatingPoint(content, lineNo, DOUBLE) { num: Double -> num as T }
135+
else -> invalidType(T::class.toString(), "Signed Type")
136+
}
137137

138138
/**
139139
* ktoml parser treats all integer literals as Long and all floating-point literals as Double,
@@ -162,19 +162,19 @@ public abstract class TomlAbstractDecoder : AbstractDecoder() {
162162
* we will check here, that your byte value does not exceed 127 and so on.
163163
*/
164164
private inline fun <reified T> decodeInteger(content: Long, lineNo: Int): T =
165-
when (T::class) {
166-
Byte::class -> validateAndConvertInteger(content, lineNo, BYTE) { it.toByte() as T }
167-
Short::class -> validateAndConvertInteger(content, lineNo, SHORT) { it.toShort() as T }
168-
Int::class -> validateAndConvertInteger(content, lineNo, INT) { it.toInt() as T }
169-
Long::class -> validateAndConvertInteger(content, lineNo, LONG) { it as T }
170-
Double::class, Float::class -> throw IllegalTypeException(
171-
"Expected floating-point number, but received integer literal: <$content>. " +
172-
"Deserialized floating-point number should have a dot: <$content.0>",
173-
lineNo
174-
)
165+
when (T::class) {
166+
Byte::class -> validateAndConvertInteger(content, lineNo, BYTE) { it.toByte() as T }
167+
Short::class -> validateAndConvertInteger(content, lineNo, SHORT) { it.toShort() as T }
168+
Int::class -> validateAndConvertInteger(content, lineNo, INT) { it.toInt() as T }
169+
Long::class -> validateAndConvertInteger(content, lineNo, LONG) { it as T }
170+
Double::class, Float::class -> throw IllegalTypeException(
171+
"Expected floating-point number, but received integer literal: <$content>. " +
172+
"Deserialized floating-point number should have a dot: <$content.0>",
173+
lineNo
174+
)
175175

176-
else -> invalidType(T::class.toString(), "Signed Type")
177-
}
176+
else -> invalidType(T::class.toString(), "Signed Type")
177+
}
178178

179179
/**
180180
* ktoml parser treats all integer literals as Long and all floating-point literals as Double,

ktoml-core/src/commonMain/kotlin/com/akuleshov7/ktoml/decoders/TomlArrayDecoder.kt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,8 +69,10 @@ public class TomlArrayDecoder(
6969
return super.beginStructure(descriptor)
7070
}
7171

72+
// decodeKeyValue is usually used for simple plain structures, but as it is present in TomlAbstractDecoder,
73+
// we should implement it and have this stub
7274
override fun decodeKeyValue(): TomlKeyValue = throw NotImplementedError("Method `decodeKeyValue`" +
73-
" should never be called for TomlListDecoder, it should use ")
75+
" should never be called for TomlArrayDecoder, because it is a more complex structure")
7476

7577
override fun decodeString(): String = currentElementDecoder.decodeString()
7678
override fun decodeInt(): Int = currentElementDecoder.decodeInt()

ktoml-core/src/commonMain/kotlin/com/akuleshov7/ktoml/decoders/TomlMainDecoder.kt

Lines changed: 48 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -58,19 +58,20 @@ public class TomlMainDecoder(
5858
}
5959

6060
override fun decodeEnum(enumDescriptor: SerialDescriptor): Int {
61-
val value = decodeValue().toString()
61+
val node = decodeKeyValue()
62+
val value = node.value.content.toString()
6263
val index = enumDescriptor.getElementIndex(value)
6364

6465
if (index == CompositeDecoder.UNKNOWN_NAME) {
65-
throw InvalidEnumValueException(value, enumDescriptor)
66+
throw InvalidEnumValueException(value, enumDescriptor, node.lineNo)
6667
}
6768

6869
return index
6970
}
7071

7172
// the iteration will go through all elements that will be found in the input
7273
private fun isDecodingDone() =
73-
if (rootNode is TomlFile) true else elementIndex == rootNode.getNeighbourNodes().size
74+
if (rootNode is TomlFile) true else elementIndex == rootNode.getNeighbourNodes().size
7475

7576
/**
7677
* Getting the node with the value
@@ -103,7 +104,7 @@ public class TomlMainDecoder(
103104
// branch, we should throw an exception as it is not expected at all and we should catch this in tests
104105
else ->
105106
throw InternalDecodingException(
106-
"Node of type [${node::class}] should not be processed in TomlDecoder.decodeValue(): <${node.content}>"
107+
"Node of type [${node::class}] should not be processed in TomlDecoder.decodeValue(): <${node.content}>."
107108
)
108109
}
109110
}
@@ -189,9 +190,9 @@ public class TomlMainDecoder(
189190

190191
if (!descriptor.isElementOptional(index)) {
191192
throw MissingRequiredPropertyException(
192-
"Invalid number of key-value arguments provided in the input for deserialization." +
193-
" Missing required property " +
194-
"<${descriptor.getElementName(index)}> from class <${descriptor.serialName}> in the input"
193+
"Invalid number of key-value arguments provided in the input for deserialization. Missing required property " +
194+
"<${descriptor.getElementName(index)}> from class <${descriptor.serialName}> in the input. " +
195+
"(In your deserialization class you have declared this field, but it is missing in the input)"
195196
)
196197
}
197198
}
@@ -201,63 +202,63 @@ public class TomlMainDecoder(
201202
* A hack that comes from a compiler plugin to process Inline (value) classes
202203
*/
203204
override fun decodeInline(inlineDescriptor: SerialDescriptor): Decoder =
204-
iterateOverStructure(inlineDescriptor, true)
205+
iterateOverStructure(inlineDescriptor, true)
205206

206207
/**
207208
* this method does all the iteration logic for processing code structures and collections
208209
* treat it as an !entry point! and the orchestrator of the decoding
209210
*/
210211
override fun beginStructure(descriptor: SerialDescriptor): CompositeDecoder =
211-
iterateOverStructure(descriptor, false)
212+
iterateOverStructure(descriptor, false)
212213

213214
/**
214215
* Entry Point into the logic, core logic of the structure traversal and linking the data from TOML AST
215216
* to the descriptor and vica-versa. Basically this logic is used to iterate through data structures and do processing.
216217
*/
217218
private fun iterateOverStructure(descriptor: SerialDescriptor, inlineFunc: Boolean): TomlAbstractDecoder =
218-
if (rootNode is TomlFile) {
219-
checkMissingRequiredProperties(rootNode.children, descriptor)
220-
val firstFileChild = getFirstChild(rootNode)
221-
222-
// inline structures has a very specific logic for decoding. Kotlinx.serialization plugin generates specific code:
223-
// 'decoder.decodeInline(this.getDescriptor()).decodeLong())'. So we need simply to increment
224-
// our element index by 1 (0 is the default value), because value/inline classes are always a wrapper over some SINGLE value.
225-
if (inlineFunc) TomlMainDecoder(firstFileChild, config, 1) else TomlMainDecoder(firstFileChild, config, 0)
226-
} else {
227-
// this is a tricky index calculation, suggest not to change. We are using the previous node to get all neighbour nodes:
228-
// | (parentNode)
229-
// |--- neighbourNodes: (current rootNode) (next node which we would like to process now)
230-
val nextProcessingNode = rootNode
231-
.getNeighbourNodes()
232-
.elementAt(elementIndex - 1)
233-
234-
when (nextProcessingNode) {
235-
is TomlKeyValueArray -> TomlArrayDecoder(nextProcessingNode, config)
236-
is TomlKeyValuePrimitive, is TomlStubEmptyNode -> TomlMainDecoder(nextProcessingNode, config)
237-
is TomlTablePrimitive -> {
238-
val firstTableChild = nextProcessingNode.getFirstChild() ?: throw InternalDecodingException(
239-
"Decoding process failed due to invalid structure of parsed AST tree: missing children" +
240-
" in a table <${nextProcessingNode.fullTableKey}>"
241-
)
242-
checkMissingRequiredProperties(firstTableChild.getNeighbourNodes(), descriptor)
243-
TomlMainDecoder(firstTableChild, config)
244-
}
245-
else -> throw InternalDecodingException(
246-
"Incorrect decoding state in the beginStructure()" +
247-
" with $nextProcessingNode ($nextProcessingNode)[${nextProcessingNode.name}]"
219+
if (rootNode is TomlFile) {
220+
checkMissingRequiredProperties(rootNode.children, descriptor)
221+
val firstFileChild = getFirstChild(rootNode)
222+
223+
// inline structures has a very specific logic for decoding. Kotlinx.serialization plugin generates specific code:
224+
// 'decoder.decodeInline(this.getDescriptor()).decodeLong())'. So we need simply to increment
225+
// our element index by 1 (0 is the default value), because value/inline classes are always a wrapper over some SINGLE value.
226+
if (inlineFunc) TomlMainDecoder(firstFileChild, config, 1) else TomlMainDecoder(firstFileChild, config, 0)
227+
} else {
228+
// this is a tricky index calculation, suggest not to change. We are using the previous node to get all neighbour nodes:
229+
// | (parentNode)
230+
// |--- neighbourNodes: (current rootNode) (next node which we would like to process now)
231+
val nextProcessingNode = rootNode
232+
.getNeighbourNodes()
233+
.elementAt(elementIndex - 1)
234+
235+
when (nextProcessingNode) {
236+
is TomlKeyValueArray -> TomlArrayDecoder(nextProcessingNode, config)
237+
is TomlKeyValuePrimitive, is TomlStubEmptyNode -> TomlMainDecoder(nextProcessingNode, config)
238+
is TomlTablePrimitive -> {
239+
val firstTableChild = nextProcessingNode.getFirstChild() ?: throw InternalDecodingException(
240+
"Decoding process has failed due to invalid structure of parsed AST tree: missing children" +
241+
" in a table <${nextProcessingNode.fullTableKey}>"
248242
)
243+
checkMissingRequiredProperties(firstTableChild.getNeighbourNodes(), descriptor)
244+
TomlMainDecoder(firstTableChild, config)
249245
}
246+
else -> throw InternalDecodingException(
247+
"Incorrect decoding state in the beginStructure()" +
248+
" with $nextProcessingNode ($nextProcessingNode)[${nextProcessingNode.name}]"
249+
)
250250
}
251+
}
251252

252253
private fun getFirstChild(node: TomlNode) =
253-
node.getFirstChild() ?: if (!config.allowEmptyToml) {
254-
throw InternalDecodingException(
255-
"Missing child nodes (tables, key-values) for TomlFile." +
256-
" Was empty toml provided to the input?"
257-
)
258-
} else {
259-
node
260-
}
254+
node.getFirstChild() ?: if (!config.allowEmptyToml) {
255+
throw InternalDecodingException(
256+
"Missing child nodes (tables, key-values) for TomlFile." +
257+
"May be an empty toml was provided to the input?"
258+
)
259+
} else {
260+
node
261+
}
261262

262263
public companion object {
263264
/**

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

Lines changed: 24 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ public abstract class TomlAbstractEncoder protected constructor(
5252

5353
if (key !is String) {
5454
throw UnsupportedEncodingFeatureException(
55-
"Arbitrary map key types are not supported. Must be either a string" +
55+
"Arbitrary map key ($key) types are not supported. Must be either a string" +
5656
" or enum. Provide a custom serializer for $type to either " +
5757
"of the supported key types."
5858
)
@@ -253,14 +253,14 @@ public abstract class TomlAbstractEncoder protected constructor(
253253
rootNode: TomlNode,
254254
attributes: TomlEncoderAttributes = this.attributes.child()
255255
): TomlAbstractEncoder =
256-
TomlArrayEncoder(
257-
rootNode,
258-
parent = this,
259-
elementIndex,
260-
attributes,
261-
outputConfig,
262-
serializersModule
263-
)
256+
TomlArrayEncoder(
257+
rootNode,
258+
parent = this,
259+
elementIndex,
260+
attributes,
261+
outputConfig,
262+
serializersModule
263+
)
264264

265265
/**
266266
* Creates a new inline table encoder instance, with the encoder as its parent.
@@ -269,14 +269,14 @@ public abstract class TomlAbstractEncoder protected constructor(
269269
* @return The new instance.
270270
*/
271271
protected open fun inlineTableEncoder(rootNode: TomlNode): TomlAbstractEncoder =
272-
TomlInlineTableEncoder(
273-
rootNode,
274-
parent = this,
275-
elementIndex,
276-
attributes.child(),
277-
outputConfig,
278-
serializersModule
279-
)
272+
TomlInlineTableEncoder(
273+
rootNode,
274+
parent = this,
275+
elementIndex,
276+
attributes.child(),
277+
outputConfig,
278+
serializersModule
279+
)
280280

281281
/**
282282
* Creates a new table encoder instance.
@@ -285,11 +285,11 @@ public abstract class TomlAbstractEncoder protected constructor(
285285
* @return The new instance.
286286
*/
287287
protected open fun tableEncoder(rootNode: TomlNode): TomlAbstractEncoder =
288-
TomlMainEncoder(
289-
rootNode,
290-
elementIndex,
291-
attributes.child(),
292-
outputConfig,
293-
serializersModule
294-
)
288+
TomlMainEncoder(
289+
rootNode,
290+
elementIndex,
291+
attributes.child(),
292+
outputConfig,
293+
serializersModule
294+
)
295295
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ public data class TomlEncoderAttributes(
3232
public var inlineComment: String = "",
3333
public var isImplicit: Boolean = false,
3434
) {
35-
public fun keyOrThrow(): String = key ?: throw InternalEncodingException("Key not set")
35+
public fun keyOrThrow(): String = key ?: throw InternalEncodingException("Key is not set")
3636

3737
public fun child(): TomlEncoderAttributes = copy(parent = copy(), isImplicit = false)
3838

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,7 @@ public class TomlInlineTableEncoder internal constructor(
152152
child.comments,
153153
child.inlineComment
154154
)
155-
else -> throw InternalEncodingException("Not a pair")
155+
else -> throw InternalEncodingException("Tried to encode $child as a pair, but it has unknown type.")
156156
}
157157
}
158158
}

0 commit comments

Comments
 (0)