<commit_before>package net.bjoernpetersen.musicbot.api.config

import mu.KotlinLogging
import net.bjoernpetersen.musicbot.spi.config.ConfigChecker
import net.bjoernpetersen.musicbot.spi.config.ConfigStorageAdapter

class Config internal constructor(
    private val adapter: ConfigStorageAdapter,
    private val scope: ConfigScope) {

    private val logger = KotlinLogging.logger {}
    private val entries: MutableMap<String, String> = adapter.load(scope).toMutableMap()

    internal fun dump() {
        // TODO delet this
        println(entries)
    }

    private fun getValue(key: String): String? {
        return entries[key]?.let {
            if (it.isBlank()) null
            else it
        }
    }

    // TODO listeners

    private fun setValue(key: String, value: String?) {
        val old = getValue(key)
        if (old != value) {
            logger.debug {
                "Config[$scope] entry '$key' changed"
            }
            if (value.isNullOrBlank()) {
                entries.remove(key)
            } else {
                entries[key] = value
            }
            adapter.store(scope, entries)
        }
    }

    abstract inner class Entry<T> internal constructor(
        val key: String,
        val description: String,
        val uiNode: UiNode<in T>?) {

        abstract fun getWithoutDefault(): T?
        abstract fun get(): T?
        abstract fun set(value: T?)
        abstract fun checkError(): String?
    }

    inner class StringEntry @JvmOverloads constructor(
        key: String,
        description: String,
        private val configChecker: ConfigChecker<in String>,
        uiNode: UiNode<in String>? = null,
        val default: String? = null) : Entry<String>(key, description, uiNode) {

        override fun getWithoutDefault(): String? {
            return getValue(key)
        }

        override fun get(): String? {
            return getWithoutDefault() ?: default
        }

        override fun set(value: String?) {
            setValue(key, value)
        }

        override fun checkError(): String? = configChecker(get())
    }

    open inner class SerializedEntry<T> @JvmOverloads constructor(
        key: String,
        description: String,
        private val serializer: ConfigSerializer<T>,
        private val configChecker: ConfigChecker<in T>,
        uiNode: UiNode<in T>? = null,
        val default: T? = null) : Entry<T>(key, description, uiNode) {

        override fun getWithoutDefault(): T? = try {
            getValue(key)?.let { serializer.deserialize(it) }
        } catch (e: SerializationException) {
            logger.error(e) { "Deserialization failure" }
            setValue(key, null)
            null
        }

        @Throws(SerializationException::class)
        override fun get(): T? = getWithoutDefault() ?: default

        override fun set(value: T?) {
            setValue(key, value?.let { serializer.serialize(it) })
        }

        override fun checkError(): String? = configChecker(getWithoutDefault())<commit_msg>Pass default value to SerializedEntry checker<commit_after>package net.bjoernpetersen.musicbot.api.config

import mu.KotlinLogging
import net.bjoernpetersen.musicbot.spi.config.ConfigChecker
import net.bjoernpetersen.musicbot.spi.config.ConfigStorageAdapter

class Config internal constructor(
    private val adapter: ConfigStorageAdapter,
    private val scope: ConfigScope) {

    private val logger = KotlinLogging.logger {}
    private val entries: MutableMap<String, String> = adapter.load(scope).toMutableMap()

    internal fun dump() {
        // TODO delet this
        println(entries)
    }

    private fun getValue(key: String): String? {
        return entries[key]?.let {
            if (it.isBlank()) null
            else it
        }
    }

    // TODO listeners

    private fun setValue(key: String, value: String?) {
        val old = getValue(key)
        if (old != value) {
            logger.debug {
                "Config[$scope] entry '$key' changed"
            }
            if (value.isNullOrBlank()) {
                entries.remove(key)
            } else {
                entries[key] = value
            }
            adapter.store(scope, entries)
        }
    }

    abstract inner class Entry<T> internal constructor(
        val key: String,
        val description: String,
        val uiNode: UiNode<in T>?) {

        abstract fun getWithoutDefault(): T?
        abstract fun get(): T?
        abstract fun set(value: T?)
        abstract fun checkError(): String?
    }

    inner class StringEntry @JvmOverloads constructor(
        key: String,
        description: String,
        private val configChecker: ConfigChecker<in String>,
        uiNode: UiNode<in String>? = null,
        val default: String? = null) : Entry<String>(key, description, uiNode) {

        override fun getWithoutDefault(): String? {
            return getValue(key)
        }

        override fun get(): String? {
            return getWithoutDefault() ?: default
        }

        override fun set(value: String?) {
            setValue(key, value)
        }

        override fun checkError(): String? = configChecker(get())
    }

    open inner class SerializedEntry<T> @JvmOverloads constructor(
        key: String,
        description: String,
        private val serializer: ConfigSerializer<T>,
        private val configChecker: ConfigChecker<in T>,
        uiNode: UiNode<in T>? = null,
        val default: T? = null) : Entry<T>(key, description, uiNode) {

        override fun getWithoutDefault(): T? = try {
            getValue(key)?.let { serializer.deserialize(it) }
        } catch (e: SerializationException) {
            logger.error(e) { "Deserialization failure" }
            setValue(key, null)
            null
        }

        @Throws(SerializationException::class)
        override fun get(): T? = getWithoutDefault() ?: default

        override fun set(value: T?) {
            setValue(key, value?.let { serializer.serialize(it) })
        }

        override fun checkError(): String? = configChecker(get())
{'fraction_non_alphanumeric': 0.10225416530545574, 'fraction_numerical': 0.0, 'mean_word_length': 2.1890625, 'pattern_counts': {'":': 0, '<': 25, '<?xml version=': 0, '>': 25, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4332616', 'n_tokens_mistral': 1673, 'n_tokens_neox': 1646, 'n_words': 448}