decoder

inline fun <T : TomlValue, R : Any> decoder(noinline decoder: TomlDecoder.(targetType: KType, tomlValue: T) -> R?)

Configures a custom decoder function for the given Kotlin type. A custom decoder function is a function from a TomlValue and a KType representing a target type, to that target type. Custom decoder functions are associated with a KClass representing that target type.

When a TOML value is decoded to some target type, the decoder will look for all decoder functions associated with that type. All decoder functions matching that type are then tried in reverse order of registration, from newest to oldest. I.e. for some decoder D = tomlMapper { decoder<T>(A) ; decoder<T>(B) }, B will always be tried before A when trying to decode values of type T.

A decoder function can signal that they are unable to decode their given input by calling TomlDecoder.pass. When this happens, the decoder will go on to try the next relevant decoder, if any.

Binding decoder functions to a KClass rather than a KType, while allowing the decoder function to access that KType, allows for more fine-grained control over deserialization. Let's say, for instance, that you have a custom data structure, generic in its elements, that you want to decode TOML values into.

If a decoder function was bound to a KType, you would need to register one decoder function for MyDataStructure<Int>, one for MyDataStructure<String>, etc. - a lot of unnecessary boilerplate.

If a decoder function was bound to a KClass and did not have access to the corresponding KType, you would have no way of knowing the type of the elements of the data structure. You would instead be forced to rely on the default decoding of TOML values - TomlValue.Integer into Long, TomlValue.Map into Map, and so on - an unacceptable loss of functionality.

A decoder function with access to the target type's KType, bound to the target type's KClass gets the best of both worlds. As an example, here is how you would create a custom decoder function for the generic data structure used in the above paragraphs.


val mapper = tomlMapper {
decoder { kType, tomlValue: TomlValue.List ->
val myDataStructure = MyDataStructure<Any>()
tomlValue.forEach {
decode(it, kType.arguments.single().type!!)
myDataStructure.add(convertedElement)
}
myDataStructure
}

val result = mapper.decode<MyDataStructure<SomeType>>(Path.of("path", "to", "file.toml"))


inline fun <T : TomlValue, R : Any> decoder(crossinline decoder: TomlDecoder.(tomlValue: T) -> R?)

Convenience overload for decoder, for when you don't need to consider the full target KType.


fun <T : Any> decoder(kClass: KClass<T>, decoder: TomlDecoder.(targetType: KType, tomlValue: TomlValue) -> Any?)