-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
11 changed files
with
192 additions
and
8 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
32 changes: 32 additions & 0 deletions
32
core/src/commonMain/kotlin/app/meetacy/di/builder/ScopedBuilder.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
package app.meetacy.di.builder | ||
|
||
import app.meetacy.di.DI | ||
import app.meetacy.di.dependency.DependencyProvider | ||
import app.meetacy.di.scope.Scope | ||
import app.meetacy.di.scope.scopeOf | ||
|
||
public class ScopedBuilder<T> { | ||
private var scope: Scope? = null | ||
private var factory: DependencyProvider<T>? = null | ||
|
||
public fun keepWhile( | ||
block: ScopeBuilder.() -> Scope.Dependency | ||
) { | ||
scope = scopeOf { ScopeBuilder(it).run(block) } | ||
} | ||
|
||
public fun factory( | ||
block: DI.() -> T | ||
) { | ||
factory = DependencyProvider(block) | ||
} | ||
|
||
public fun build(): DependencyProvider<T> = DependencyProvider.Scoped( | ||
scope ?: error("Scope was not initialized, call keepWhile function to setup scope"), | ||
factory ?: error("Factory was not initialized, call factory function to setup factory") | ||
) | ||
} | ||
|
||
public class ScopeBuilder(public val di: DI) { | ||
public fun retained(vararg any: Any?): Scope.Dependency = Scope.Dependency(any.toList()) | ||
} |
38 changes: 35 additions & 3 deletions
38
core/src/commonMain/kotlin/app/meetacy/di/dependency/DependencyProvider.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,20 +1,52 @@ | ||
package app.meetacy.di.dependency | ||
|
||
import app.meetacy.di.DI | ||
import app.meetacy.di.internal.AtomicReference | ||
import app.meetacy.di.internal.updateAndGet | ||
import app.meetacy.di.scope.Scope | ||
|
||
public fun interface DependencyProvider<out T> { | ||
|
||
public fun createNewInstance(di: DI): T | ||
public fun getInstance(di: DI): T | ||
|
||
public class Singleton<out T>( | ||
private val base: DependencyProvider<T> | ||
) : DependencyProvider<T> { | ||
private lateinit var di: DI | ||
private val value by lazy { base.createNewInstance(di) } | ||
private val value by lazy { base.getInstance(di) } | ||
|
||
override fun createNewInstance(di: DI): T { | ||
override fun getInstance(di: DI): T { | ||
this.di = di | ||
return value | ||
} | ||
} | ||
|
||
public class Scoped<out T>( | ||
private val scope: Scope, | ||
private val base: DependencyProvider<T> | ||
) : DependencyProvider<T> { | ||
private val reference = AtomicReference<Value<T>?>(value = null) | ||
|
||
override fun getInstance(di: DI): T = reference.updateAndGet { value -> | ||
val scopeDependency = scope.dependency.getInstance(di) | ||
|
||
// if nothing is initialized, we should initialize one | ||
if (value == null) { | ||
val instance = base.getInstance(di) | ||
return@updateAndGet Value(scopeDependency, instance) | ||
} | ||
|
||
// if there was already something in there, we should check if scope dependency is the same | ||
if (value.currentScope == scopeDependency) return@updateAndGet value | ||
|
||
// dependency changed, so we need to refresh factory | ||
val instance = base.getInstance(di) | ||
Value(scopeDependency, instance) | ||
}.instance | ||
|
||
private class Value<out T>( | ||
val currentScope: Scope.Dependency, | ||
val instance: T | ||
) | ||
} | ||
} |
13 changes: 13 additions & 0 deletions
13
core/src/commonMain/kotlin/app/meetacy/di/internal/AtomicReference.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
package app.meetacy.di.internal | ||
|
||
internal expect class AtomicReference<T>(value: T) { | ||
var value: T | ||
fun compareAndSet(expected: T, new: T): Boolean | ||
} | ||
|
||
internal inline fun <T, T2 : T> AtomicReference<T>.updateAndGet(transform: (T) -> T2): T2 { | ||
while (true) { | ||
val new = transform(value) | ||
if (compareAndSet(value, new)) return new | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
package app.meetacy.di.scope | ||
|
||
import app.meetacy.di.dependency.DependencyKey | ||
import app.meetacy.di.dependency.DependencyProvider | ||
|
||
/** | ||
* As long as the dependencies do not change, the scope remains. | ||
* When dependencies change, the new dependency that scoped to this scope | ||
* will be created. | ||
*/ | ||
public data class Scope( | ||
public val dependency: DependencyProvider<Dependency> | ||
) { | ||
public data class Dependency(public val underlying: Any?) { | ||
override fun toString(): String = "Scope.Dependency(underlying=$underlying)" | ||
} | ||
} | ||
|
||
public fun scopeOf(provider: DependencyProvider<*>): Scope { | ||
return Scope { di -> Scope.Dependency(provider.getInstance(di)) } | ||
} | ||
|
||
public fun scopeOf(key: DependencyKey<*>): Scope { | ||
return Scope { di -> Scope.Dependency(di.get(key)) } | ||
} |
14 changes: 14 additions & 0 deletions
14
core/src/iosMain/kotlin/app/meetacy/di/internal/AtomicReference.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
package app.meetacy.di.internal | ||
|
||
import kotlin.native.concurrent.AtomicReference | ||
|
||
internal actual class AtomicReference<T> actual constructor( | ||
value: T | ||
) { | ||
private val delegate = AtomicReference(value) | ||
|
||
actual var value: T by delegate::value | ||
|
||
actual fun compareAndSet(expected: T, new: T): Boolean = | ||
delegate.compareAndSet(expected, new) | ||
} |
14 changes: 14 additions & 0 deletions
14
core/src/jsMain/kotlin/app/meetacy/di/internal/AtomicReference.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
package app.meetacy.di.internal | ||
|
||
/** | ||
* For now, JS is single-threaded | ||
*/ | ||
internal actual class AtomicReference<T> actual constructor( | ||
actual var value: T | ||
) { | ||
actual fun compareAndSet(expected: T, new: T): Boolean { | ||
if (value !== expected) return false | ||
value = new | ||
return true | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
14 changes: 14 additions & 0 deletions
14
core/src/jvmMain/kotlin/app/meetacy/di/internal/AtomicReference.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
package app.meetacy.di.internal | ||
|
||
import java.util.concurrent.atomic.AtomicReference | ||
|
||
internal actual class AtomicReference<T> actual constructor(value: T) { | ||
private val delegate = AtomicReference(value) | ||
|
||
actual var value: T | ||
get() = delegate.get() | ||
set(value) = delegate.set(value) | ||
|
||
actual fun compareAndSet(expected: T, new: T): Boolean = | ||
delegate.compareAndSet(expected, new) | ||
} |