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

commonMain.io.polywrap.configBuilder.BaseConfigBuilder.kt Maven / Gradle / Ivy

package io.polywrap.configBuilder

import io.polywrap.client.PolywrapClient
import io.polywrap.core.WrapEnv
import io.polywrap.core.WrapPackage
import io.polywrap.core.Wrapper
import io.polywrap.core.msgpack.EnvSerializer
import io.polywrap.core.msgpack.msgPackDecode
import io.polywrap.core.msgpack.msgPackEncode
import io.polywrap.core.resolution.UriResolver
import kotlinx.serialization.serializer
import kotlin.collections.Map

/**
 * Defines a builder for creating configured [PolywrapClient] instances with support for
 * adding, removing, and modifying various configuration options.
 */
abstract class BaseConfigBuilder {

    /**
     * Holds the current configuration being built.
     */
    val config = BuilderConfig(
        envs = mutableMapOf(),
        interfaces = mutableMapOf(),
        redirects = mutableMapOf(),
        wrappers = mutableMapOf(),
        packages = mutableMapOf(),
        resolvers = mutableListOf(),
        ffiBundles = mutableListOf()
    )

    /**
     * Builds a [PolywrapClient] instance with the current builder configuration and
     * optional additional configuration specified through a builder DSL.
     *
     * @return A [PolywrapClient] instance with the specified configuration.
     */
    abstract fun build(): PolywrapClient

    /**
     * Adds all default configuration [Bundle]s to the current configuration.
     *
     * @return This [BaseConfigBuilder] instance for chaining calls.
     */
    abstract fun addDefaults(): BaseConfigBuilder

    /**
     * Adds a [NativeBundle] from the FFI.
     * Bundles defined in the FFI are always added to the final configuration before all other items,
     * and therefore can be overwritten regardless of the order in which they are added.
     *
     * @return This [BaseConfigBuilder] instance for chaining calls.
     */
    fun addBundle(bundle: NativeBundle): BaseConfigBuilder = this.apply {
        config.ffiBundles.add(bundle)
    }

    /**
     * Adds a [Bundle] to the current configuration.
     *
     * @return This [BaseConfigBuilder] instance for chaining calls.
     */
    fun addBundle(bundle: Bundle): BaseConfigBuilder = this.apply {
        bundle.items.forEach { (uri, item) ->
            item.pkg?.let { setPackage(uri to it) }
            item.implements?.forEach { addInterfaceImplementation(it.toString(), uri) }
            item.redirectFrom?.forEach { setRedirect(it.toString() to uri) }
            item.env?.let { addEnv(uri to it) }
        }
    }

    /**
     * Adds the given [BuilderConfig] to the current configuration.
     *
     * @param config The [BuilderConfig] to add.
     * @return This [BaseConfigBuilder] instance for chaining calls.
     */
    fun add(config: BuilderConfig): BaseConfigBuilder = this.apply {
        config.envs.forEach { this.config.envs[validateUri(it.key)] = it.value }
        config.redirects.forEach { setRedirect(it.toPair()) }
        config.wrappers.forEach { setWrapper(it.toPair()) }
        config.packages.forEach { setPackage(it.toPair()) }
        config.interfaces.forEach { (interfaceUri, implementations) ->
            addInterfaceImplementations(interfaceUri, implementations.toList())
        }
        addResolvers(config.resolvers)
    }

    /**
     * Adds a wrapper with a specified URI key to the current configuration.
     *
     * @param wrapper A [Pair] of the URI key and the [Wrapper] to add.
     * @return This [BaseConfigBuilder] instance for chaining calls.
     */
    fun setWrapper(wrapper: Pair): BaseConfigBuilder = this.apply {
        config.wrappers[validateUri(wrapper.first)] = wrapper.second
    }

    /**
     * Adds a set of wrappers with specified URI keys to the current configuration.
     *
     * @param wrappers A [Map] of URI keys to [Wrapper] instances to add.
     * @return This [BaseConfigBuilder] instance for chaining calls.
     */
    fun setWrappers(wrappers: Map): BaseConfigBuilder = this.apply {
        wrappers.forEach { setWrapper(it.toPair()) }
    }

    /**
     * Removes a wrapper with the specified URI key from the current configuration.
     *
     * @param uri The URI key of the wrapper to remove.
     * @return This [BaseConfigBuilder] instance for chaining calls.
     */
    fun removeWrapper(uri: String): BaseConfigBuilder = this.apply {
        config.wrappers.remove(validateUri(uri))
    }

    /**
     * Adds a package with a specified URI key to the current configuration.
     *
     * @param wrapPackage A [Pair] of the URI key and the [WrapPackage] to add.
     * @return This [BaseConfigBuilder] instance for chaining calls.
     */
    fun setPackage(wrapPackage: Pair): BaseConfigBuilder = this.apply {
        config.packages[validateUri(wrapPackage.first)] = wrapPackage.second
    }

    /**
     * Adds a set of packages with specified URI keys to the current configuration.
     *
     * @param packages A [Map] of URI keys to [WrapPackage] instances to add.
     * @return This [BaseConfigBuilder] instance for chaining calls.
     */
    fun setPackages(packages: Map): BaseConfigBuilder = this.apply {
        packages.forEach { setPackage(it.toPair()) }
    }

    /**
     * Removes a package with the specified URI key from the current configuration.
     *
     * @param uri The URI key of the package to remove.
     * @return This [BaseConfigBuilder] instance for chaining calls.
     */
    fun removePackage(uri: String): BaseConfigBuilder = this.apply {
        config.packages.remove(validateUri(uri))
    }

    /**
     * Adds an environment variable with a specified URI key to the current configuration.
     *
     * @param env A [Pair] of the URI key and the [WrapEnv] or serializable Env class instance to add.
     * @return This [BaseConfigBuilder] instance for chaining calls.
     */
    inline fun addEnv(env: Pair): BaseConfigBuilder = this.apply {
        val sanitizedUri = validateUri(env.first)
        val existingEnv = config.envs[sanitizedUri]
        if (existingEnv != null) {
            val previous: WrapEnv = msgPackDecode(EnvSerializer, existingEnv).getOrThrow()
            val new = if (E::class == Map::class) {
                validateMapAsEnv(env.second as Map<*, *>)
            } else {
                val newEnv = msgPackEncode(serializer(), env.second)
                msgPackDecode(EnvSerializer, newEnv).getOrThrow()
            }
            config.envs[sanitizedUri] = msgPackEncode(EnvSerializer, previous + new)
        } else {
            val newEnv = if (E::class == Map::class) {
                val wrapEnv = validateMapAsEnv(env.second as Map<*, *>)
                msgPackEncode(EnvSerializer, wrapEnv)
            } else {
                msgPackEncode(serializer(), env.second)
            }
            config.envs[sanitizedUri] = newEnv
        }
    }

    /**
     * Adds a set of environment variables with specified URI keys to the current configuration.
     *
     * @param envs A [Map] of URI keys to [WrapEnv] or serializable Env class instances to add.
     * @return This [BaseConfigBuilder] instance for chaining calls.
     */
    inline fun addEnvs(envs: Map): BaseConfigBuilder = this.apply {
        envs.forEach { env -> addEnv(env.toPair()) }
    }

    /**
     * Removes an environment variable with the specified URI key from the current configuration.
     *
     * @param uri The URI key of the environment variable to remove.
     * @return This [BaseConfigBuilder] instance for chaining calls.
     */
    fun removeEnv(uri: String): BaseConfigBuilder = this.apply {
        config.envs.remove(validateUri(uri))
    }

    /**
     * Sets or replaces an environment variable with a specified URI key in the current configuration.
     *
     * @param env A [Pair] of the URI key and the [WrapEnv] or serializable Env class instance to set.
     * @return This [BaseConfigBuilder] instance for chaining calls.
     */
    inline fun setEnv(env: Pair): BaseConfigBuilder = this.apply {
        config.envs[validateUri(env.first)] = if (E::class == Map::class) {
            val newEnv = validateMapAsEnv(env.second as Map<*, *>)
            msgPackEncode(EnvSerializer, newEnv)
        } else {
            msgPackEncode(serializer(), env.second)
        }
    }

    /**
     * Adds an interface implementation with the specified interface and implementation URIs.
     *
     * @param interfaceUri The URI of the interface to associate with the implementation.
     * @param implementationUri The URI of the implementation.
     * @return This [BaseConfigBuilder] instance for chaining calls.
     */
    fun addInterfaceImplementation(
        interfaceUri: String,
        implementationUri: String
    ): BaseConfigBuilder = this.apply {
        val sanitizedInterfaceUri = validateUri(interfaceUri)
        val existingInterface = this.config.interfaces[sanitizedInterfaceUri]

        if (existingInterface != null) {
            existingInterface.add(validateUri(implementationUri))
        } else {
            this.config.interfaces[sanitizedInterfaceUri] = mutableSetOf(validateUri(implementationUri))
        }
    }

    /**
     * Adds multiple interface implementations with the specified interface URI and a list of implementation URIs.
     *
     * @param interfaceUri The URI of the interface to associate with the implementations.
     * @param implementationUris A [List] of URIs of the implementations.
     * @return This [BaseConfigBuilder] instance for chaining calls.
     */
    fun addInterfaceImplementations(
        interfaceUri: String,
        implementationUris: List
    ): BaseConfigBuilder = this.apply {
        val sanitizedInterfaceUri = validateUri(interfaceUri)
        val existingInterface = this.config.interfaces[sanitizedInterfaceUri]

        if (existingInterface != null) {
            implementationUris.forEach { existingInterface.add(validateUri(it)) }
        } else {
            val sanitizedImplUris = implementationUris.map { validateUri(it) }
            this.config.interfaces[sanitizedInterfaceUri] = sanitizedImplUris.toMutableSet()
        }
    }

    /**
     * Removes an interface implementation with the specified interface and implementation URIs.
     *
     * @param interfaceUri The URI of the interface associated with the implementation.
     * @param implementationUri The URI of the implementation to remove.
     * @return This [BaseConfigBuilder] instance for chaining calls.
     */
    fun removeInterfaceImplementation(
        interfaceUri: String,
        implementationUri: String
    ): BaseConfigBuilder = this.apply {
        val sanitizedInterfaceUri = validateUri(interfaceUri)
        val existingInterface = this.config.interfaces[sanitizedInterfaceUri] ?: return this

        existingInterface.remove(validateUri(implementationUri))

        if (existingInterface.isEmpty()) {
            this.config.interfaces.remove(sanitizedInterfaceUri)
        }
    }

    /**
     * Adds a redirect with a specified source and destination URI.
     *
     * @param redirect A [Pair] of the source URI and the destination URI.
     * @return This [BaseConfigBuilder] instance for chaining calls.
     */
    fun setRedirect(redirect: Pair): BaseConfigBuilder = this.apply {
        this.config.redirects[validateUri(redirect.first)] = validateUri(redirect.second)
    }

    /**
     * Adds a set of redirects with specified source and destination URIs.
     *
     * @param redirects A [Map] of source URIs to destination URIs.
     * @return This [BaseConfigBuilder] instance for chaining calls.
     */
    fun setRedirects(redirects: Map): BaseConfigBuilder = this.apply {
        redirects.forEach { setRedirect(it.toPair()) }
    }

    /**
     * Removes a redirect with the specified source URI.
     *
     * @param from The source URI of the redirect to remove.
     * @return This [BaseConfigBuilder] instance for chaining calls.
     */
    fun removeRedirect(from: String): BaseConfigBuilder = this.apply {
        this.config.redirects.remove(validateUri(from))
    }

    /**
     * Adds a [UriResolver] to the current configuration.
     *
     * @param resolver The [UriResolver] instance to add.
     * @return This [BaseConfigBuilder] instance for chaining calls.
     */
    fun addResolver(resolver: UriResolver): BaseConfigBuilder = this.apply {
        this.config.resolvers.add(resolver)
    }

    /**
     * Adds a list of [UriResolver] instances to the current configuration.
     *
     * @param resolvers A [List] of [UriResolver] instances to add.
     * @return This [BaseConfigBuilder] instance for chaining calls.
     */
    fun addResolvers(resolvers: List): BaseConfigBuilder = this.apply {
        resolvers.forEach { addResolver(it) }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy