All Downloads are FREE. Search and download functionalities are using the official Maven repository.

commonMain.com.akuleshov7.ktoml.Toml.kt Maven / Gradle / Ivy

package com.akuleshov7.ktoml

import com.akuleshov7.ktoml.decoders.TomlMainDecoder
import com.akuleshov7.ktoml.encoders.TomlMainEncoder
import com.akuleshov7.ktoml.exceptions.MissingRequiredPropertyException
import com.akuleshov7.ktoml.parsers.TomlParser
import com.akuleshov7.ktoml.tree.nodes.TomlFile
import com.akuleshov7.ktoml.utils.findPrimitiveTableInAstByName
import com.akuleshov7.ktoml.writers.TomlWriter
import kotlin.native.concurrent.ThreadLocal
import kotlinx.serialization.DeserializationStrategy
import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.SerializationStrategy
import kotlinx.serialization.StringFormat
import kotlinx.serialization.modules.EmptySerializersModule
import kotlinx.serialization.modules.SerializersModule

/**
 * Toml class - is a general entry point in the core,
 * that is used to serialize/deserialize TOML file or string
 *
 * @property inputConfig - configuration for deserialization
 * @property outputConfig - configuration for serialization
 * @property serializersModule - default overridden
 */
@OptIn(ExperimentalSerializationApi::class)
public open class Toml(
    protected val inputConfig: TomlInputConfig = TomlInputConfig(),
    protected val outputConfig: TomlOutputConfig = TomlOutputConfig(),
    override val serializersModule: SerializersModule = EmptySerializersModule(),
) : StringFormat {
    // parser and writer are created once after the creation of the class, to reduce
    // the number of created parsers and writers for each toml
    public val tomlParser: TomlParser = TomlParser(inputConfig)
    public val tomlWriter: TomlWriter = TomlWriter(outputConfig)

    // ================== basic overrides ===============

    /**
     * simple deserializer of a string in a toml format (separated by newlines)
     *
     * @param string - request-string in toml format with '\n' or '\r\n' separation
     * @return deserialized object of type T
     */
    override fun  decodeFromString(deserializer: DeserializationStrategy, string: String): T {
        val parsedToml = tomlParser.parseString(string)
        return TomlMainDecoder.decode(deserializer, parsedToml, inputConfig)
    }

    override fun  encodeToString(serializer: SerializationStrategy, value: T): String {
        val toml = TomlMainEncoder.encode(serializer, value, outputConfig, serializersModule)
        return tomlWriter.writeToString(file = toml)
    }

    // ================== custom decoding methods ===============

    /**
     * simple deserializer of a list of strings in a toml format
     *
     * @param toml list with strings in toml format
     * @param deserializer deserialization strategy
     * @param config
     * @return deserialized object of type T
     */
    @Deprecated(
        message = "List of strings as an input was replaced with a sequence, " +
                "to avoid extra conversion use sequence instead",
        replaceWith = ReplaceWith(
            "decodeFromString",
            "com.akuleshov7.ktoml.decodeFromString"
        )
    )
    public fun  decodeFromString(
        deserializer: DeserializationStrategy,
        toml: List,
        config: TomlInputConfig = this.inputConfig
    ): T = decodeFromString(deserializer, toml.asSequence(), config)

    /**
     * simple deserializer of a sequence of strings in a toml format
     *
     * @param toml sequence with strings in toml format
     * @param deserializer deserialization strategy
     * @param config
     * @return deserialized object of type T
     */
    public fun  decodeFromString(
        deserializer: DeserializationStrategy,
        toml: Sequence,
        config: TomlInputConfig = this.inputConfig
    ): T {
        val parsedToml = tomlParser.parseStringsToTomlTree(toml, config)
        return TomlMainDecoder.decode(deserializer, parsedToml, this.inputConfig)
    }

    /**
     * partial deserializer of a sequence of lines in a toml format.
     * Will deserialize only the part presented under the tomlTableName table.
     * If such table is missing in he input - will throw an exception
     *
     * (!) Useful when you would like to deserialize only ONE table
     * and you do not want to reproduce whole object structure in the code
     *
     * @param deserializer deserialization strategy
     * @param tomlLines sequence of TOML lines
     * @param tomlTableName fully qualified name of the toml table (it should be the full name -  a.b.c.d)
     * @param config
     * @return deserialized object of type T
     */
    public fun  partiallyDecodeFromLines(
        deserializer: DeserializationStrategy,
        tomlLines: Sequence,
        tomlTableName: String,
        config: TomlInputConfig = this.inputConfig
    ): T {
        val fakeFileNode = generateFakeTomlStructureForPartialParsing(tomlLines, tomlTableName, config, TomlParser::parseLines)
        return TomlMainDecoder.decode(deserializer, fakeFileNode, this.inputConfig)
    }

    /**
     * partial deserializer of a string in a toml format (separated by newlines).
     * Will deserialize only the part presented under the tomlTableName table.
     * If such table is missing in he input - will throw an exception
     *
     * (!) Useful when you would like to deserialize only ONE table
     * and you do not want to reproduce whole object structure in the code
     *
     * @param deserializer deserialization strategy
     * @param toml request-string in toml format with '\n' or '\r\n' separation
     * @param tomlTableName fully qualified name of the toml table (it should be the full name -  a.b.c.d)
     * @param config
     * @return deserialized object of type T
     */
    public fun  partiallyDecodeFromString(
        deserializer: DeserializationStrategy,
        toml: String,
        tomlTableName: String,
        config: TomlInputConfig = this.inputConfig
    ): T {
        val fakeFileNode = generateFakeTomlStructureForPartialParsing(toml, tomlTableName, config, TomlParser::parseString)
        return TomlMainDecoder.decode(deserializer, fakeFileNode, config)
    }

    /**
     * partial deserializer of a sequence of lines in a toml format.
     * Will deserialize only the part presented under the tomlTableName table.
     * If such table is missing in he input - will throw an exception
     *
     * (!) Useful when you would like to deserialize only ONE table
     * and you do not want to reproduce whole object structure in the code
     *
     * @param deserializer deserialization strategy
     * @param tomlLines sequence of strings with toml input
     * @param tomlTableName fully qualified name of the toml table (it should be the full name -  a.b.c.d)
     * @param config
     * @return deserialized object of type T
     */
    public fun  partiallyDecodeFromString(
        deserializer: DeserializationStrategy,
        tomlLines: Sequence,
        tomlTableName: String,
        config: TomlInputConfig = this.inputConfig
    ): T {
        val fakeFileNode = generateFakeTomlStructureForPartialParsing(
            tomlLines,
            tomlTableName,
            config,
            TomlParser::parseLines,
        )
        return TomlMainDecoder.decode(deserializer, fakeFileNode, this.inputConfig)
    }

    // ================== other ===============
    @Suppress("TYPE_ALIAS")
    private fun  generateFakeTomlStructureForPartialParsing(
        tomlInput: I,
        tomlTableName: String,
        config: TomlInputConfig = TomlInputConfig(),
        parsingFunction: (TomlParser, I) -> TomlFile
    ): TomlFile {
        val tomlFile = parsingFunction(TomlParser(config), tomlInput)
        val parsedToml = findPrimitiveTableInAstByName(listOf(tomlFile), tomlTableName)
            ?: throw MissingRequiredPropertyException(
                "Cannot find table with name <$tomlTableName> in the toml input. " +
                        " Are you sure that this table exists in the input?" +
                        " Not able to decode this toml part."
            )

        // adding a fake file node to restore the structure and parse only the part of te toml
        val fakeFileNode = TomlFile()
        parsedToml.children.forEach {
            fakeFileNode.appendChild(it)
        }

        return fakeFileNode
    }

    /**
     * The default instance of [Toml] with the default configuration.
     * See [TomlConfig] for the list of the default options
     * ThreadLocal annotation is used here for caching.
     */
    @ThreadLocal
    public companion object Default : Toml(
        inputConfig = TomlInputConfig(),
        outputConfig = TomlOutputConfig()
    )
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy