commonMain.at.asitplus.signum.supreme.dsl.ConfigurationDSL.kt Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of supreme-jvm Show documentation
Show all versions of supreme-jvm Show documentation
Kotlin Multiplatform Crypto Provider
package at.asitplus.signum.supreme.dsl
/**
* The meta functionality that enables us to easily create DSLs.
* @see at.asitplus.signum.supreme.dsl.DSLInheritanceDemonstration
* @see at.asitplus.signum.supreme.dsl.DSLVarianceDemonstration
*/
object DSL {
/** Resolve a DSL lambda to a concrete configuration */
fun resolve(factory: ()->T, config: DSLConfigureFn): T =
(if (config == null) factory() else factory().apply(config)).also(Data::validate)
sealed interface Holder {
val v: T
}
sealed interface Invokable: Holder {
operator fun invoke(configure: Target.()->Unit)
}
/** Constructed by: [DSL.Data.child]. */
class DirectHolder internal constructor(default: T, private val factory: ()->(T & Any))
: Invokable {
private var _v: T = default
override val v: T get() = _v
override operator fun invoke(configure: (T & Any).()->Unit) { _v = resolve(factory, configure) }
}
/** Constructed by: [DSL.Data.subclassOf]. */
class Generalized internal constructor(default: T): Holder {
private var _v: T = default
override val v: T get() = _v
inner class option
/**
* Adds a specialized invokable accessor for the underlying generalized storage.
* Use as `val specialized = _holder.option(::SpecializedClass).`
*
* User code can invoke this specialized accessor as `specialized { }`.
* This constructs a new specialized child, configures it using the specified block,
* and stores it in the underlying generalized storage.
*/
internal constructor(private val factory: ()->S) : Invokable {
override val v: T get() = [email protected]
override operator fun invoke(configure: S.()->Unit) { _v = resolve(factory, configure) }
}
}
/** Constructed by: [DSL.Data.integratedReceiver]. */
class Integrated internal constructor(): Invokable<(T.() -> Unit)?, T> {
private var _v: (T.()->Unit)? = null
override val v: (T.()->Unit)? get() = _v
override operator fun invoke(configure: T.()->Unit) { _v = configure }
}
@DslMarker
annotation class Marker
/** The superclass of all DSL configuration objects. Exposes helper functions for definition. */
@Marker
open class Data {
/**
* Embeds a child; use as `val sub = child(::TypeOfSub)`.
* Defaults to a default-constructed child.
*
* User code will invoke as `child { }`.
* This constructs a new child and configures it using the specified block.
*/
protected fun child(factory: ()->T): Invokable =
DirectHolder(factory(), factory)
/**
* Embeds an optional child. Use as `val sub = childOrNull(::TypeOfSub)`.
* Defaults to `null`.
*
* User code will invoke as `child { }`
* This constructs a new child and configures it using the specified block.
*/
protected fun childOrNull(factory: ()->T): Invokable =
DirectHolder(null, factory)
/**
* Specifies a generalized holder of type T.
* Use as `internal val _subHolder = subclassOf()`.
*
* The generalized holder itself cannot be invoked, and should be marked `internal`.
* Defaults to `null`.
*
* Specialized invokable accessors can be spun off via `.option(::SpecializedClass)`.
* @see DSL.Generalized.option
*/
protected fun subclassOf(): Generalized =
Generalized(null)
/**
* Specifies a generalized holder of type T.
* Use as `internal val _subHolder = subclassOf(SpecializedClass())`.
*
* The generalized holder itself cannot be invoked, and should be marked `internal`.
* Defaults to the specified `default`.
*
* Specialized invokable accessors can be spun off via `.option(::SpecializedClass)`.
* @see DSL.Generalized.option
*/
protected fun subclassOf(default: T): Generalized =
Generalized(default)
/**
* Integrates an external configuration lambda into the DSL.
* Use as `val other = integratedReceiver()`.
*
* This receiver can be invoked, but simply stores the received lambda instead of running it.
* Defaults to `null`.
*/
protected fun integratedReceiver(): Integrated =
Integrated()
/**
* Invoked by `DSL.resolve()` after the configuration block runs.
* Can be used for sanity checks.
*/
internal open fun validate() {}
}
}
typealias DSLConfigureFn = (T.()->Unit)?