11package com.mineinabyss.features
22
3+ import com.mineinabyss.features.DI.Module
4+ import com.mineinabyss.features.impl.ModuleImpl
5+ import com.mineinabyss.features.impl.MutableDIImpl
36import kotlin.reflect.KType
47import kotlin.reflect.typeOf
58
9+ interface DIAware : AutoCloseable {
10+ val di: DI
11+
12+ override fun close () {
13+ di.close()
14+ }
15+ }
16+
617/* *
718 * A dependency injection context, values are evaluated lazily.
819 *
920 * A value *provider* can never change, though calling [Get] twice may return different values
1021 * when using a factory provider.
1122 */
1223@FeatureDSLMarker
13- interface DI {
24+ interface DI : DIAware , AutoCloseable {
25+ val scope: DIScope
26+
27+ override val di: DI get() = this
28+
1429 fun <T > Get (type : Pair <KType , String ?>): T
15- fun <T > Lazy (type : Pair <KType , String ?>): Lazy <T >
30+ fun <T > Lazy (type : Pair <KType , String ?>): InjectedValue <T >
31+
32+ fun addCloseable (closeable : AutoCloseable )
1633
1734 val injected: List <Pair <Pair <KType , String ?>, InjectedValue <* >>>
35+
36+ interface Module {
37+ val name: String
38+ val key: Key
39+
40+ fun create (parent : DI ): DI
41+
42+ fun override (beforeLoad : MutableDI .() -> Unit ): Module
43+
44+ interface Key
45+ }
46+
47+ fun childDI (): DI {
48+ val di = invoke(di.scope) { import(this @DI.di) }
49+ addCloseable(di)
50+ return di
51+ }
52+
53+ interface ModuleWithConfig <T > : Module {
54+ fun get (parent : DI ): T
55+ }
56+
57+ companion object {
58+ operator fun invoke (scope : DIScope = DIScope (), builder : MutableDI .() -> Unit ): DI {
59+ return MutableDIImpl (scope).apply (builder)
60+ }
61+ }
62+ }
63+
64+ fun module (
65+ name : String ,
66+ block : MutableDI .() -> Unit ,
67+ ): Module {
68+ return ModuleImpl (name, configure = block)
69+ }
70+
71+ inline fun DIAware.addCloseable (crossinline closeable : () -> Unit ) {
72+ di.addCloseable(AutoCloseable { closeable() })
73+ }
74+
75+ fun <T : AutoCloseable > DIAware.addCloseable (closeable : T ): T {
76+ di.addCloseable(closeable)
77+ return closeable
78+ }
79+
80+ fun DIAware.addCloseables (vararg closeables : AutoCloseable ) {
81+ closeables.forEach { addCloseable(it) }
82+ }
83+
84+ inline fun <reified T > Module.gets (): DI .ModuleWithConfig <T > {
85+ return object : DI .ModuleWithConfig <T >, Module by this {
86+ override fun get (parent : DI ): T {
87+ return parent.get<T >()
88+ }
89+ }
1890}
1991
2092/* *
@@ -23,8 +95,35 @@ interface DI {
2395 * @see DI
2496 */
2597interface MutableDI : DI {
26- fun <T > Put (type : Pair <KType , String ?>, property : InjectedValue <T >): Lazy <T >
98+ fun <T > Put (type : Pair <KType , String ?>, property : InjectedValue <T >): InjectedValue <T >
2799
100+ /* *
101+ * Loads a [DI.Module] as a singleton in this DI [scope]. If a module is already loaded, gets the instance.
102+ *
103+ * Closes this DI context when the included module is unloaded in [scope].
104+ */
105+ fun singleModule (di : Module ): DI
106+
107+ /* *
108+ * Loads a [DI.Module] inside this [DI] context, copies dependencies at the point of the call into the submodule,
109+ * but does not [import] the submodule's new dependencies into this context.
110+ *
111+ * Closes the submodule when closing this DI context.
112+ *
113+ * @see import
114+ */
115+ fun submodule (di : Module ): DI
116+
117+ /* *
118+ * Imports all keys from a DI [context] into this context.
119+ * Keys can never be overridden, so this may throw an error if the included context contains clashing providers.
120+ *
121+ * Identical providers do not throw an error, thus a submodule can be included via:
122+ *
123+ * ```kotlin
124+ * import(submodule(other))
125+ * ```
126+ */
28127 fun import (context : DI )
29128}
30129
@@ -33,15 +132,18 @@ interface MutableDI : DI {
33132 *
34133 * Immediately throws an error if not already registered.
35134 */
36- inline fun <reified T > DI.get (key : String? = null): T = Get (typeOf<T >() to key)
135+ inline fun <reified T > DIAware.get (key : String? = null): T = di.Get (typeOf<T >() to key)
136+
137+ // TODO simplify
138+ inline fun <reified T > DIAware.getOrNull (key : String? = null): T ? = runCatching { di.Get <T >(typeOf<T >() to key) }.getOrNull()
37139
38140/* *
39141 * Gets a value of type [T], optionally keyed by [key] as a delegate.
40142 * Only evaluates when first read.
41143 *
42144 * Immediately throws an error if not already registered.
43145 */
44- inline fun <reified T > DI .getLazy (key : String? = null): Lazy <T > = Lazy (typeOf<T >() to key)
146+ inline fun <reified T > DIAware .getLazy (key : String? = null): InjectedValue <T > = di. Lazy (typeOf<T >() to key)
45147
46148/* *
47149 * Registers a value of type [T], optionally keyed by [key].
@@ -53,9 +155,9 @@ inline fun <reified T> MutableDI.single(
53155 key : String? = null,
54156 ignoreOverride : Boolean = false,
55157 crossinline block : DI .() -> T ,
56- ): Lazy <T > = Put (
158+ ): InjectedValue <T > = Put (
57159 typeOf<T >() to key,
58- InjectedValue (ignoreOverride, lazy { block() })
160+ InjectedValueImpl (ignoreOverride, lazy { block() })
59161)
60162
61163/* *
@@ -69,10 +171,10 @@ inline fun <reified T> MutableDI.factory(
69171 key : String? = null,
70172 ignoreOverride : Boolean = false,
71173 noinline block : DI .() -> T ,
72- ): Lazy <T > {
174+ ): InjectedValue <T > {
73175 return Put (
74176 typeOf<T >() to key,
75- InjectedValue (ignoreOverride, object : Lazy <T > {
177+ InjectedValueImpl (ignoreOverride, object : Lazy <T > {
76178 override val value: T get() = block()
77179
78180 override fun isInitialized (): Boolean = false
@@ -81,6 +183,6 @@ inline fun <reified T> MutableDI.factory(
81183}
82184
83185context(context: MutableDI )
84- inline fun <reified R > Lazy <R>.and (): Lazy <R > {
85- return context.single<R > { this @and.value }
186+ inline fun <reified R > InjectedValue <R>.and (): InjectedValue <R > {
187+ return context.single<R >(ignoreOverride = ignoreOverride) { this @and.value }
86188}
0 commit comments