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

commonMain.io.ktor.util.converters.DataConversion.kt Maven / Gradle / Ivy

Go to download

Ktor is a framework for quickly creating web applications in Kotlin with minimal effort.

The newest version!
/*
 * Copyright 2014-2021 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
 */

package io.ktor.util.converters

import io.ktor.util.*
import io.ktor.util.reflect.*
import io.ktor.utils.io.*
import kotlin.reflect.*

/**
 * Data conversion plugin to serialize and deserialize types using [converters] registry
 */
public class DataConversion(configuration: Configuration) : ConversionService {
    private val converters: Map, ConversionService> = configuration.converters.toMap()

    override fun fromValues(values: List, type: TypeInfo): Any? {
        if (values.isEmpty()) {
            return null
        }
        val converter = converters[type.type] ?: DefaultConversionService
        return converter.fromValues(values, type)
    }

    override fun toValues(value: Any?): List {
        val type: KClass<*> = value?.let { it::class } ?: return listOf()
        val converter = converters[type] ?: DefaultConversionService
        return converter.toValues(value)
    }

    /**
     * Data conversion service configuration
     */
    @KtorDsl
    public class Configuration {
        internal val converters = mutableMapOf, ConversionService>()

        /**
         * Register a [convertor] for [type] type
         */
        public fun convert(type: KClass<*>, convertor: ConversionService) {
            converters[type] = convertor
        }

        /**
         * Register and [configure] convertor for type [klass]
         */
        @Suppress("UNCHECKED_CAST")
        public fun  convert(type: KType, configure: DelegatingConversionService.Configuration.() -> Unit) {
            val klass = type.classifier as KClass
            val configuration = DelegatingConversionService.Configuration(klass).apply(configure)

            val service = DelegatingConversionService(
                klass,
                configuration.decoder,
                configuration.encoder as ((Any?) -> List)?
            )
            convert(klass, service)
        }

        /**
         * Register and [configure] convertor for reified type [T]
         */
        public inline fun  convert(
            noinline configure: DelegatingConversionService.Configuration.() -> Unit
        ): Unit = convert(typeOf(), configure)
    }
}

/**
 * Implementation of [ConversionService] that delegates [fromValues] and [toValues] to [decoder] and [encoder]
 */
public class DelegatingConversionService(
    private val klass: KClass<*>,
    private val decoder: ((values: List) -> Any?)?,
    private val encoder: ((value: Any?) -> List)?,
) : ConversionService {

    override fun fromValues(values: List, type: TypeInfo): Any? {
        val currentDecoder = decoder ?: throw IllegalStateException("Decoder was not specified for type '$klass'")
        return currentDecoder(values)
    }

    override fun toValues(value: Any?): List {
        val currentDecoder = encoder ?: throw IllegalStateException("Encoder was not specified for type '$klass'")
        return currentDecoder(value)
    }

    /**
     * Custom convertor builder to be used in [DataConversion.Configuration]
     */
    public class Configuration @PublishedApi internal constructor(internal val klass: KClass) {

        internal var decoder: ((values: List) -> T)? = null
        internal var encoder: ((value: T) -> List)? = null

        /**
         * Configure decoder function. Only one decoder could be supplied
         * @throws IllegalStateException
         */
        public fun decode(converter: (values: List) -> T) {
            if (decoder != null) throw IllegalStateException("Decoder has already been set for type '$klass'")
            decoder = converter
        }

        /**
         * Configure encoder function. Only one encoder could be supplied
         * @throws IllegalStateException
         */
        public fun encode(converter: (value: T) -> List) {
            if (encoder != null) throw IllegalStateException("Encoder has already been set for type '$klass'")
            encoder = converter
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy