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

tech.codingzen.kdi.data_structure.Kdi.kt Maven / Gradle / Ivy

There is a newer version: 2.1.0
Show newest version
package tech.codingzen.kdi.data_structure

import tech.codingzen.kdi.InternalKdi
import tech.codingzen.kdi.KdiException
import tech.codingzen.kdi.ScopeId
import tech.codingzen.kdi.Tag
import tech.codingzen.kdi.data_structure.ScopeExtenderType.POST
import tech.codingzen.kdi.data_structure.ScopeExtenderType.PRE
import tech.codingzen.kdi.dsl.ScopeDsl
import tech.codingzen.kdi.dsl.builder.kdi_app.KdiAppSpecBuilders
import tech.codingzen.kdi.dsl.builder.kdi_app.ScopeSpecBuilders
import tech.codingzen.kdi.dsl.module.ModuleDsl
import tech.codingzen.kdi.dsl.module.MultipleModulesDsl
import tech.codingzen.kdi.logging.Logger
import tech.codingzen.kdi.spec.*
import java.util.*


class Kdi(
  private val extensions: KdiExtensions,
  val logger: Logger,
  private val scopes: ScopesList
) {
  companion object {
    @InternalKdi
    val fqcn = Kdi::class.qualifiedName

    /**
     * default tag for components that do not have a specific tag
     */
    val defaultTag: Tag = "___default-tag___"

    fun appSpec(name: String = "app") = KdiAppSpecBuilders.LoggerStep(name)

    fun scopeSpec(id: ScopeId) = ScopeSpecBuilders.BootstrapperStep(id)

    fun moduleSpec(id: ScopeId, block: ModuleDsl.() -> Unit): BaseSpec =
      scopeSpec(id)
        .noBootstrapper()
        .module(block)
        .build()

    fun modulesSpec(id: ScopeId, block: MultipleModulesDsl.() -> Unit): BaseSpec =
      scopeSpec(id)
        .noBootstrapper()
        .modules(block)
        .build()

    /**
     * Create a component descriptor
     *
     * @param T component type
     * @param tag uniquely identifies components with the same fqcn.  Optional parameter that defaults to [Kdi.defaultTag]
     * @return descriptor for type [T]
     */
    inline fun  descriptor(tag: Tag = defaultTag): Descriptor {
      val fqcn = T::class.qualifiedName
        ?: throw IllegalStateException("Cannot create a component without a qualified name")
      return Descriptor(fqcn, tag)
    }
  }

  /**
   * Using the current 'scope' (held by an instance of Kdi) to create a child scope defined by [spec].  The created
   * scope is then accessed via ScopeDsl and [executor].
   *
   * @param T result type
   * @param spec scope specification to be created in the context of this Kdi instance
   * @param executor uses the created scope to compute a result of type T
   * @returns the result of applying [executor] to the built scope defined by [spec]
   */
  suspend fun  execute(spec: ScopeSpec, executor: ScopeExecutor): T {
    val builtScopes = when (spec) {
      is BaseSpec -> createScope(scopes, spec)
      is MultipleSpec -> {
        var acc: ScopesList = scopes
        val stack = Stack().apply { spec.specs.asReversed().forEach { push(it) } }
        while (stack.isNotEmpty()) {
          when (val current = stack.pop()) {
            is BaseSpec -> acc = createScope(acc, current)
            is MultipleSpec -> current.specs.asReversed().forEach { stack.push(it) }
          }
        }
        acc
      }
    }

    return executor.execute(ScopeDsl(Kdi(extensions, logger, builtScopes), builtScopes))
  }

  private fun copy(scopes: ScopesList) = Kdi(extensions, logger, scopes)

  private suspend fun createScope(parent: ScopesList, base: BaseSpec): ScopesList {
    val preScope = parent + Scope("${base.id}-pre-scope", base.preModules)
    val preExtenders = (extensions.scopeExtensions[base.id]?.get(PRE) ?: listOf())
    val extendedPreScope =
      if (preExtenders.isEmpty()) preScope
      else run {
        val scopeDsl = ScopeDsl(copy(preScope), preScope)
        val modules = try {
          preExtenders.flatMap { it(scopeDsl).create() }
        } catch (exc: Exception) {
          throw KdiException("pre ScopeExtender failed", exc)
        }
        preScope + Scope("${base.id}-pre-scope-extended", modules)
      }

    val runtimeModules = try {
      base.bootstrapper(ScopeDsl(copy(extendedPreScope), extendedPreScope)).create()
    } catch (exc: Exception) {
      throw KdiException("bootstrapper for scope: ${base.id} failed", exc)
    }
    val bootstrappedScope = extendedPreScope + Scope("${base.id}-bootstrapped-scope", runtimeModules)

    val postScope = bootstrappedScope + Scope("${base.id}-post-scope", base.postModules)
    val postExtenders = (extensions.scopeExtensions[base.id]?.get(POST) ?: listOf())
    val extendedPostScope =
      if (postExtenders.isEmpty()) postScope
      else run {
        val scopeDsl = ScopeDsl(copy(postScope), postScope)
        val modules = try {
          postExtenders.flatMap { it(scopeDsl).create() }
        } catch (exc: Exception) {
          throw KdiException("pre ScopeExtender failed", exc)
        }
        postScope + Scope("${base.id}-post-scope-extended", modules)
      }

    return extendedPostScope
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy