commonMain.it.unibo.pulvreakt.dsl.PulverizationScope.kt Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of core-jvm Show documentation
Show all versions of core-jvm Show documentation
A framework to create a pulverized system
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