Skip to content

Commit

Permalink
Split config into input and output parts (#143)
Browse files Browse the repository at this point in the history
* Split TomlConfig into input and output parts

* Update config examples in README.md

* Fix a straggling reference to TomlConfig

* Remove TomlCommonConfig

* Resolve post-merge conflicts

* Add removal warnings to deprecations

* Fix import ordering
  • Loading branch information
NightEule5 authored May 25, 2022
1 parent 443d8d3 commit 3126f03
Show file tree
Hide file tree
Showing 32 changed files with 639 additions and 157 deletions.
8 changes: 5 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -183,8 +183,8 @@ val resultFromList = TomlFileReader.partiallyDecodeFromFile<MyClass>(serializer(
import com.akuleshov7.ktoml.parsers.TomlParser
import com.akuleshov7.ktoml.TomlConfig
/* ========= */
var tomlAST = TomlParser(TomlConfig()).parseStringsToTomlTree(/* list with toml strings */)
tomlAST = TomlParser(TomlConfig()).parseString(/* the string that you want to parse */)
var tomlAST = TomlParser(TomlInputConfig()).parseStringsToTomlTree(/* list with toml strings */)
tomlAST = TomlParser(TomlInputConfig()).parseString(/* the string that you want to parse */)
tomlAST.prettyPrint()
```
</details>
Expand All @@ -195,7 +195,7 @@ special configuration class that can be passed to the decoder method:

```kotlin
Toml(
config = TomlConfig(
inputConfig = TomlInputConfig(
// allow/prohibit unknown names during the deserialization, default false
ignoreUnknownNames = false,
// allow/prohibit empty values like "a = # comment", default true
Expand All @@ -206,6 +206,8 @@ Toml(
allowEscapedQuotesInLiteralStrings = true,
// allow/prohibit processing of empty toml, if false - throws an InternalDecodingException exception, default is true
allowEmptyToml = true,
),
outputConfig = TomlOutputConfig(
// indentation symbols for serialization, default 4 spaces
indentation = Indentation.FOUR_SPACES,
)
Expand Down
88 changes: 72 additions & 16 deletions ktoml-core/src/commonMain/kotlin/com/akuleshov7/ktoml/Toml.kt
Original file line number Diff line number Diff line change
Expand Up @@ -8,31 +8,43 @@ 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 config - configuration for the serialization
* @property inputConfig - configuration for deserialization
* @property outputConfig - configuration for serialization
* @property serializersModule - default overridden
*/
@OptIn(ExperimentalSerializationApi::class)
public open class Toml(
private val config: TomlConfig = TomlConfig(),
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(config)
public val tomlWriter: TomlWriter = TomlWriter(config)
public val tomlParser: TomlParser = TomlParser(inputConfig)
public val tomlWriter: TomlWriter = TomlWriter(outputConfig)

@Deprecated(
message = "config parameter split into inputConfig and outputConfig. Will be removed in next releases."
)
public constructor(
config: TomlConfig,
serializersModule: SerializersModule = EmptySerializersModule
) : this(
config.input,
config.output,
serializersModule
)

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

Expand All @@ -44,7 +56,7 @@ public open class Toml(
*/
override fun <T> decodeFromString(deserializer: DeserializationStrategy<T>, string: String): T {
val parsedToml = tomlParser.parseString(string)
return TomlMainDecoder.decode(deserializer, parsedToml, config)
return TomlMainDecoder.decode(deserializer, parsedToml, inputConfig)
}

override fun <T> encodeToString(serializer: SerializationStrategy<T>, value: T): String {
Expand All @@ -64,12 +76,25 @@ public open class Toml(
public fun <T> decodeFromString(
deserializer: DeserializationStrategy<T>,
toml: List<String>,
config: TomlConfig
config: TomlInputConfig
): T {
val parsedToml = tomlParser.parseStringsToTomlTree(toml, config)
return TomlMainDecoder.decode(deserializer, parsedToml, this.config)
return TomlMainDecoder.decode(deserializer, parsedToml, config)
}

@Deprecated(
message = "TomlConfig is deprecated; use TomlInputConfig instead. Will be removed in next releases.",
replaceWith = ReplaceWith(
"decodeFromString(deserializer, toml, config)",
"com.akuleshov7.ktoml.TomlInputConfig"
)
)
public fun <T> decodeFromString(
deserializer: DeserializationStrategy<T>,
toml: List<String>,
config: TomlConfig
): T = decodeFromString(deserializer, toml, config.input)

/**
* partial deserializer of a string in a toml format (separated by newlines).
* Will deserialize only the part presented under the tomlTableName table.
Expand All @@ -88,12 +113,26 @@ public open class Toml(
deserializer: DeserializationStrategy<T>,
toml: String,
tomlTableName: String,
config: TomlConfig = TomlConfig()
config: TomlInputConfig = TomlInputConfig()
): T {
val fakeFileNode = generateFakeTomlStructureForPartialParsing(toml, tomlTableName, config, TomlParser::parseString)
return TomlMainDecoder.decode(deserializer, fakeFileNode, this.config)
return TomlMainDecoder.decode(deserializer, fakeFileNode, config)
}

@Deprecated(
message = "TomlConfig is deprecated; use TomlInputConfig instead. Will be removed in next releases.",
replaceWith = ReplaceWith(
"partiallyDecodeFromString(deserializer, toml, tomlTableName, config)",
"com.akuleshov7.ktoml.TomlInputConfig"
)
)
public fun <T> partiallyDecodeFromString(
deserializer: DeserializationStrategy<T>,
toml: String,
tomlTableName: String,
config: TomlConfig
): T = partiallyDecodeFromString(deserializer, toml, tomlTableName, config.input)

/**
* partial deserializer of a string in a toml format (separated by newlines).
* Will deserialize only the part presented under the tomlTableName table.
Expand All @@ -112,26 +151,40 @@ public open class Toml(
deserializer: DeserializationStrategy<T>,
toml: List<String>,
tomlTableName: String,
config: TomlConfig = TomlConfig()
config: TomlInputConfig = TomlInputConfig()
): T {
val fakeFileNode = generateFakeTomlStructureForPartialParsing(
toml.joinToString("\n"),
tomlTableName,
config,
TomlParser::parseString,
)
return TomlMainDecoder.decode(deserializer, fakeFileNode, this.config)
return TomlMainDecoder.decode(deserializer, fakeFileNode, config)
}

@Deprecated(
message = "TomlConfig is deprecated; use TomlInputConfig instead. Will be removed in next releases.",
replaceWith = ReplaceWith(
"partiallyDecodeFromString(deserializer, toml, tomlTableName, config)",
"com.akuleshov7.ktoml.TomlInputConfig"
)
)
public fun <T> partiallyDecodeFromString(
deserializer: DeserializationStrategy<T>,
toml: List<String>,
tomlTableName: String,
config: TomlConfig = TomlConfig()
): T = partiallyDecodeFromString(deserializer, toml, tomlTableName, config.input)

// ================== other ===============
@Suppress("TYPE_ALIAS")
private fun generateFakeTomlStructureForPartialParsing(
toml: String,
tomlTableName: String,
config: TomlConfig = TomlConfig(),
config: TomlInputConfig = TomlInputConfig(),
parsingFunction: (TomlParser, String) -> TomlFile
): TomlFile {
val tomlFile = parsingFunction(TomlParser(this.config), toml)
val tomlFile = parsingFunction(TomlParser(config), toml)
val parsedToml = findPrimitiveTableInAstByName(listOf(tomlFile), tomlTableName)
?: throw MissingRequiredPropertyException(
"Cannot find table with name <$tomlTableName> in the toml input. " +
Expand All @@ -154,5 +207,8 @@ public open class Toml(
* ThreadLocal annotation is used here for caching.
*/
@ThreadLocal
public companion object Default : Toml(TomlConfig())
public companion object Default : Toml(
inputConfig = TomlInputConfig(),
outputConfig = TomlOutputConfig()
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ public class KtomlConf(
* @property indentation - the number of spaces in the indents for the serialization
* @property allowEmptyToml - controls if empty toml can be processed, if false - will throw an exception
*/
@Deprecated(
message = "Class split into TomlInputConfig and TomlOutputConfig. Will be removed in next releases."
)
public open class TomlConfig(
public val ignoreUnknownNames: Boolean = false,
public val allowEmptyValues: Boolean = true,
Expand All @@ -32,14 +35,110 @@ public open class TomlConfig(
public val indentation: Indentation = Indentation.FOUR_SPACES,
public val allowEmptyToml: Boolean = true,
) {
internal val input = TomlInputConfig(
ignoreUnknownNames,
allowEmptyValues,
allowNullValues,
allowEmptyToml,
allowEscapedQuotesInLiteralStrings
)
internal val output = TomlOutputConfig(
indentation.toTomlIndentation(),
allowEscapedQuotesInLiteralStrings
)

/**
* @property value - string with indents, used for the formatting of serialization
*/
@Deprecated(
message = "Enum moved to top-level.",
replaceWith = ReplaceWith(
"TomlIndentation",
"com.akuleshov7.ktoml.TomlIndentation"
)
)
public enum class Indentation(public val value: String) {
FOUR_SPACES(" "),
NONE(""),
TAB("\t"),
TWO_SPACES(" "),
;

internal fun toTomlIndentation() = TomlIndentation.valueOf(name)
}
}

/**
* A config to change parsing behavior.
* @property ignoreUnknownNames Whether to allow/prohibit unknown names during the deserialization
* @property allowEmptyValues Whether to allow/prohibit empty values: a = # comment
* @property allowNullValues Whether to allow/prohibit null values: a = null
* @property allowEmptyToml Whether empty toml can be processed, if false - will throw an exception
* @property allowEscapedQuotesInLiteralStrings Whether to allow/prohibit escaping of single quotes in literal strings
*/
public data class TomlInputConfig(
public val ignoreUnknownNames: Boolean = false,
public val allowEmptyValues: Boolean = true,
public val allowNullValues: Boolean = true,
public val allowEmptyToml: Boolean = true,
public val allowEscapedQuotesInLiteralStrings: Boolean = true
) {
public companion object {
/**
* Creates a config populated with values compliant with the TOML spec.
*
* @param ignoreUnknownNames Whether to allow/prohibit unknown names during the deserialization
* @param allowEmptyToml Whether empty toml can be processed, if false - will throw an exception
* @return A TOML spec-compliant input config
*/
public fun compliant(
ignoreUnknownNames: Boolean = false,
allowEmptyToml: Boolean = true
): TomlInputConfig =
TomlInputConfig(
ignoreUnknownNames,
allowEmptyValues = false,
allowNullValues = false,
allowEmptyToml,
allowEscapedQuotesInLiteralStrings = false
)
}
}

/**
* A config to change writing behavior.
*
* @property indentation The number of spaces in the indents for the serialization
* @property allowEscapedQuotesInLiteralStrings Whether to allow/prohibit escaping of single quotes in literal strings
*/
public data class TomlOutputConfig(
public val indentation: TomlIndentation = TomlIndentation.FOUR_SPACES,
public val allowEscapedQuotesInLiteralStrings: Boolean = true,
) {
public companion object {
/**
* Creates a config populated with values compliant with the TOML spec.
*
* @param indentation The number of spaces in the indents for the serialization
* @return A TOML spec-compliant output config
*/
public fun compliant(
indentation: TomlIndentation = TomlIndentation.FOUR_SPACES
): TomlOutputConfig =
TomlOutputConfig(
indentation,
allowEscapedQuotesInLiteralStrings = false
)
}
}

/**
* @property value The indent string, used for the formatting during serialization
*/
public enum class TomlIndentation(public val value: String) {
FOUR_SPACES(" "),
NONE(""),
TAB("\t"),
TWO_SPACES(" "),
;
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,8 @@
package com.akuleshov7.ktoml.decoders

import com.akuleshov7.ktoml.TomlConfig
import com.akuleshov7.ktoml.tree.TomlKeyValue
import com.akuleshov7.ktoml.tree.TomlKeyValueArray
import com.akuleshov7.ktoml.tree.TomlKeyValuePrimitive
import com.akuleshov7.ktoml.tree.TomlNull
import com.akuleshov7.ktoml.tree.TomlValue
import com.akuleshov7.ktoml.TomlInputConfig
import com.akuleshov7.ktoml.tree.*
import kotlinx.serialization.DeserializationStrategy
import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.descriptors.SerialDescriptor
Expand All @@ -21,14 +18,22 @@ import kotlinx.serialization.modules.SerializersModule
@Suppress("UNCHECKED_CAST")
public class TomlArrayDecoder(
private val rootNode: TomlKeyValueArray,
private val config: TomlConfig,
private val config: TomlInputConfig,
) : TomlAbstractDecoder() {
private var nextElementIndex = 0
private val list = rootNode.value.content as List<TomlValue>
override val serializersModule: SerializersModule = EmptySerializersModule
private lateinit var currentElementDecoder: TomlPrimitiveDecoder
private lateinit var currentPrimitiveElementOfArray: TomlValue

@Deprecated(
message = "TomlConfig is deprecated; use TomlInputConfig instead. Will be removed in next releases."
)
public constructor(
rootNode: TomlKeyValueArray,
config: TomlConfig
) : this(rootNode, config.input)

private fun haveStartedReadingElements() = nextElementIndex > 0

override fun decodeCollectionSize(descriptor: SerialDescriptor): Int = list.size
Expand Down
Loading

0 comments on commit 3126f03

Please sign in to comment.