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

commonMain.it.unibo.pulvreakt.dsl.PulverizationScope.kt Maven / Gradle / Ivy

There is a newer version: 0.10.0
Show newest version
package it.unibo.pulvreakt.dsl

import arrow.core.Either
import arrow.core.Nel
import arrow.core.NonEmptySet
import arrow.core.raise.either
import arrow.core.raise.ensure
import arrow.core.raise.ensureNotNull
import arrow.core.raise.zipOrAccumulate
import arrow.core.toNonEmptySetOrNull
import it.unibo.pulvreakt.api.communication.protocol.Protocol
import it.unibo.pulvreakt.api.infrastructure.Host
import it.unibo.pulvreakt.dsl.deployment.DeploymentSpecificationScope
import it.unibo.pulvreakt.dsl.errors.ConfigurationError
import it.unibo.pulvreakt.dsl.errors.ConfigurationValidator
import it.unibo.pulvreakt.dsl.errors.EmptyDeploymentConfiguration
import it.unibo.pulvreakt.dsl.errors.EmptySystemConfiguration
import it.unibo.pulvreakt.dsl.errors.SystemConfigurationError
import it.unibo.pulvreakt.dsl.model.ConfiguredDevicesRuntimeConfiguration
import it.unibo.pulvreakt.dsl.model.DeviceSpecification
import it.unibo.pulvreakt.dsl.model.DeviceStructure
import it.unibo.pulvreakt.dsl.model.PulvreaktConfiguration
import it.unibo.pulvreakt.dsl.system.CanonicalDeviceScope
import it.unibo.pulvreakt.dsl.system.ExtendedDeviceScope
import kotlin.jvm.JvmInline
import kotlin.properties.ReadOnlyProperty

/**
 * Represents the type specification of a logic device.
 * It does not contain any information about the device itself, but is only used to identify the device configuration.
 * [name] is the name of the device retrieved from the property name.
 * Do not use this class directly.
 */
@JvmInline
value class LogicDeviceType(val name: String)

/**
 * Configuration DSL scope for configuring the pulverization.
 */
class PulverizationScope {
    private val systemConfigSpec = mutableSetOf, DeviceStructure>>()
    private var deploymentConfigSpec: Either, ConfiguredDevicesRuntimeConfiguration>? = null
    private lateinit var protocol: Protocol
    private var infrastructure: NonEmptySet? = null

    /**
     * Configure a logic device.
     */
    fun  logicDevice(
        config: CanonicalDeviceScope.() -> Unit,
    ): ReadOnlyProperty = ReadOnlyProperty { _, property ->
        val deviceScope = CanonicalDeviceScope(property.name).apply(config)
        systemConfigSpec.add(deviceScope.generate())
        LogicDeviceType(property.name)
    }

    /**
     * Configure an extended logic device.
     */
    fun extendedLogicDevice(config: ExtendedDeviceScope.() -> Unit): ReadOnlyProperty = ReadOnlyProperty { _, property ->
        val deviceScope = ExtendedDeviceScope(property.name).apply(config)
        systemConfigSpec.add(deviceScope.generate())
        LogicDeviceType(property.name)
    }

    /**
     * DSL scope for configuring the pulverization deployment.
     */
    fun deployment(
        infrastructure: NonEmptySet,
        protocol: Protocol,
        deploymentConfig: DeploymentSpecificationScope.() -> Unit,
    ) {
        this.protocol = protocol
        this.infrastructure = infrastructure
        val result = either {
            val deploymentScope = DeploymentSpecificationScope().apply(deploymentConfig)
            deploymentScope.generate().bind()
        }
        deploymentConfigSpec = result
    }

    internal fun generate(): Either, PulvreaktConfiguration> = either {
        zipOrAccumulate(
            { ensure(systemConfigSpec.isNotEmpty()) { EmptySystemConfiguration } },
            { ensureNotNull(deploymentConfigSpec) { EmptyDeploymentConfiguration } },
        ) { _, _ -> }
        val systemDevicesSpec = either { systemConfigSpec.map { it.bind() } }
            .map { it.toNonEmptySetOrNull()!! } // Safe since first check

        val (systemSpec, deploymentSpec) = zipOrAccumulate(
            { systemDevicesSpec.bind() },
            // `!!` is necessary since the property is a var and the smart cast can't be performed
            { deploymentConfigSpec!!.bind() },
        ) { systemSpec, deploymentConfig -> systemSpec to deploymentConfig }

        ConfigurationValidator(systemSpec, deploymentSpec, infrastructure!!).validate().bind()

        val devicesConfig = systemSpec
            .map { systemConfig ->
                // The use of `first` is safe since the check about the presence of the config is performed by the validator
                systemConfig to deploymentSpec.first { deploymentConfig -> deploymentConfig.deviceName == systemConfig.deviceName }
            }
            .map { (systemConfig, deploymentConfig) ->
                systemConfig.deviceName to DeviceSpecification(
                    systemConfig.deviceName,
                    systemConfig.componentsGraph,
                    systemConfig.requiredCapabilities,
                    deploymentConfig,
                )
            }.toMap()

        PulvreaktConfiguration(devicesConfig, protocol)
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy