From 08585356271719c78a1a4780067b9883fed81af6 Mon Sep 17 00:00:00 2001 From: UnknownJoe796 Date: Tue, 28 Jul 2026 12:22:34 -0600 Subject: [PATCH] Enable explicit API mode Turn on kotlin { explicitApi() } and add the visibility modifiers and explicit return types it requires across the public surface. No behavior changes. Rebased onto the reactive engine fixes. The twelve files the two branches both touched were resolved to the engine-fixes content and re-annotated from the compiler's diagnostics, rather than merged by hand - the original commit predates the cleanup commits, so its side of each conflict carried back code that no longer exists (runOnceWhileDead, skipDependencyRegistration, the old once() implementation, the throw-on-reentrancy check). ReactiveThreadCheck.kt stays deleted. The declarations new since the original commit are annotated to match: the reentrancyLimit parameter is public on TypedReactiveContext and reactive(), and `active` is public again. reentrancyLimit on Remember is a plain constructor parameter rather than a private val, following what this commit already did to useLastWhileLoading - both are only read in an initializer, so neither needs a backing field. RememberSuspending's useLastWhileLoading gets the same treatment, which the original missed. TypedReactiveContext's bodyless `companion object` needs explicit braces once it carries a modifier. The two added imports (CompletableJob, CoroutineContext) exist only to name types that were previously inferred. allTests green on JVM (140 tests), JS, and iOS simulator. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01GA6LAUN9Lpo5hyK3KVJteJ --- build.gradle.kts | 2 +- .../reactive/context/CalculationContext.kt | 14 +- .../reactive/context/CoroutineScopeHelpers.kt | 18 +-- .../reactive/context/CoroutineTools.kt | 14 +- .../context/DependencyChangeListener.kt | 26 ++-- .../reactive/context/DependencyTracker.kt | 8 +- .../reactive/context/ReactiveContext.kt | 62 ++++---- .../context/ReactiveContextSuspending.kt | 16 +-- .../lightningkite/reactive/core/AppScope.kt | 5 +- .../com/lightningkite/reactive/core/Draft.kt | 16 +-- .../reactive/core/InternalReactiveApi.kt | 2 +- .../reactive/core/MutableRemember.kt | 8 +- .../core/MutableRememberSuspending.kt | 8 +- .../reactive/core/ReactiveMutableList.kt | 12 +- .../reactive/core/ReactiveMutableMap.kt | 16 +-- .../reactive/core/ReactiveMutableSet.kt | 10 +- .../reactive/core/ReactiveState.kt | 50 +++---- .../lightningkite/reactive/core/Remember.kt | 10 +- .../reactive/core/RememberSuspending.kt | 8 +- .../lightningkite/reactive/core/abstracts.kt | 8 +- .../reactive/core/coreInterfaces.kt | 48 +++---- .../lightningkite/reactive/core/processes.kt | 10 +- .../lightningkite/reactive/core/signals.kt | 14 +- .../com/lightningkite/reactive/deprecated.kt | 52 +++---- .../reactive/extensions/Listenable.ext.kt | 8 +- .../reactive/extensions/ReactiveState.ext.kt | 2 +- .../reactive/extensions/WaitForNotNull.kt | 6 +- .../reactive/extensions/commaString.kt | 8 +- .../reactive/extensions/commonLenses.kt | 134 +++++++++--------- .../reactive/extensions/debounce.kt | 20 +-- .../reactive/extensions/helpers.kt | 44 +++--- .../reactive/lensing/LensByElement.kt | 59 ++++---- ...ensByElementAssumingSetNeverManipulates.kt | 8 +- .../reactive/lensing/abstracts.kt | 26 ++-- .../reactive/lensing/validation/IssueNode.kt | 24 ++-- .../reactive/lensing/validation/Validated.kt | 16 +-- .../lensing/validation/ValidatedDraft.kt | 4 +- .../lensing/validation/entrypoints.kt | 8 +- .../reactive/lensing/validation/helpers.kt | 72 +++++----- .../reactive/lensing/validation/validation.kt | 12 +- .../lensing/validation/validationLenses.kt | 8 +- 41 files changed, 449 insertions(+), 447 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 2dd4dbb..44247c8 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -48,7 +48,7 @@ kotlin { } } -// explicitApi = ExplicitApiMode.Warning + explicitApi() compilerOptions { freeCompilerArgs.add("-Xexpect-actual-classes") freeCompilerArgs.add("-opt-in=kotlinx.cinterop.BetaInteropApi") diff --git a/src/commonMain/kotlin/com/lightningkite/reactive/context/CalculationContext.kt b/src/commonMain/kotlin/com/lightningkite/reactive/context/CalculationContext.kt index dcbddcf..c2b08b0 100644 --- a/src/commonMain/kotlin/com/lightningkite/reactive/context/CalculationContext.kt +++ b/src/commonMain/kotlin/com/lightningkite/reactive/context/CalculationContext.kt @@ -44,7 +44,7 @@ public interface StatusListener : CoroutineContext.Element { * @param status The reactive status of the process * @return A [Release] lambda to stop listening to the process */ - fun watchBackgroundProcess(status: Reactive<*>): Release + public fun watchBackgroundProcess(status: Reactive<*>): Release /** * Called when a reactive calculation is happening in the foreground, so this listener can respond accordingly. @@ -55,7 +55,7 @@ public interface StatusListener : CoroutineContext.Element { * @param status The reactive status of the process * @return A [Release] lambda to stop listening to the process */ - fun watchForegroundProcess(status: Reactive<*>): Release = watchBackgroundProcess(status) + public fun watchForegroundProcess(status: Reactive<*>): Release = watchBackgroundProcess(status) } /** @@ -76,7 +76,7 @@ public interface StatusListener : CoroutineContext.Element { * * @param action The cleanup action to execute when the scope completes */ -fun CoroutineScope.onRemove(action: () -> Unit) { +public fun CoroutineScope.onRemove(action: () -> Unit) { coroutineContext[CoroutineName.Key] this.coroutineContext[Job]?.invokeOnCompletion { action() } } @@ -221,7 +221,7 @@ fun CoroutineScope.onRemove(action: () -> Unit) { public sealed interface ReactiveCoroutineScope : CoroutineScope @Deprecated("No longer needed", ReplaceWith("CoroutineScope")) -typealias CalculationContext = CoroutineScope +public typealias CalculationContext = CoroutineScope /** * Checks whether this [CoroutineScope]'s dispatcher is a main thread dispatcher. @@ -238,7 +238,7 @@ typealias CalculationContext = CoroutineScope * ``` */ @OptIn(ExperimentalStdlibApi::class) -val CoroutineScope.requireMainThread: Boolean get() = coroutineContext[CoroutineDispatcher.Key] is MainCoroutineDispatcher +public val CoroutineScope.requireMainThread: Boolean get() = coroutineContext[CoroutineDispatcher.Key] is MainCoroutineDispatcher /** * Executes the given [action] on the thread associated with this [CoroutineScope]'s dispatcher. @@ -277,7 +277,7 @@ val CoroutineScope.requireMainThread: Boolean get() = coroutineContext[Coroutine * @param action The action to execute on this scope's thread */ @OptIn(ExperimentalStdlibApi::class) -fun CoroutineScope.onThread(action: () -> Unit) { +public fun CoroutineScope.onThread(action: () -> Unit) { val d = coroutineContext[CoroutineDispatcher.Key] ?: return action() if (d.isDispatchNeeded(coroutineContext)) { d.dispatch(coroutineContext, Runnable(action)) @@ -293,4 +293,4 @@ fun CoroutineScope.onThread(action: () -> Unit) { * helping prevent accidental nesting of reactive contexts and providing better IDE support. */ @DslMarker -annotation class ReactiveDsl \ No newline at end of file +public annotation class ReactiveDsl \ No newline at end of file diff --git a/src/commonMain/kotlin/com/lightningkite/reactive/context/CoroutineScopeHelpers.kt b/src/commonMain/kotlin/com/lightningkite/reactive/context/CoroutineScopeHelpers.kt index dc2b320..7a73ba9 100644 --- a/src/commonMain/kotlin/com/lightningkite/reactive/context/CoroutineScopeHelpers.kt +++ b/src/commonMain/kotlin/com/lightningkite/reactive/context/CoroutineScopeHelpers.kt @@ -24,9 +24,9 @@ import kotlin.time.Duration.Companion.milliseconds * Interface for helper functions which require an additional [CoroutineScope] context. This will eventually * be removed in favor of context receivers. * */ -interface CoroutineScopeHelpers : CoroutineScope { +public interface CoroutineScopeHelpers : CoroutineScope { @ReactiveDsl - operator fun ((T) -> IGNORED).invoke(actionToCalculate: ReactiveContext.() -> T) = + public operator fun ((T) -> IGNORED).invoke(actionToCalculate: ReactiveContext.() -> T): TypedReactiveContext = this@CoroutineScopeHelpers.reactive(action = { this@invoke(actionToCalculate(this)) }) /** @@ -46,7 +46,7 @@ interface CoroutineScopeHelpers : CoroutineScope { * ``` * */ @ReactiveDsl - operator fun KMutableProperty0.invoke(actionToCalculate: ReactiveContext.() -> T) = this@CoroutineScopeHelpers.reactive(action = { set(actionToCalculate(this)) }) + public operator fun KMutableProperty0.invoke(actionToCalculate: ReactiveContext.() -> T): TypedReactiveContext = this@CoroutineScopeHelpers.reactive(action = { set(actionToCalculate(this)) }) /** @@ -69,7 +69,7 @@ interface CoroutineScopeHelpers : CoroutineScope { * actually optimize and cut out the overhead of a full `reactive` context. * */ @ReactiveDsl - infix fun KMutableProperty0.bind(reactive: Reactive) { + public infix fun KMutableProperty0.bind(reactive: Reactive) { if (reactive is ReactiveValue) { // I did benchmarks, this is just as fast as overloading and easier to use. val release = reactive.addAndRunListener { this@bind.set(reactive.value) } // no need for status listener since result is infallible @@ -89,7 +89,7 @@ interface CoroutineScopeHelpers : CoroutineScope { * Changes to either reactive value will propagate to the other. */ @ReactiveDsl - infix fun MutableReactive.bind(master: MutableReactive) { + public infix fun MutableReactive.bind(master: MutableReactive) { val reportTo = RawReactive(ReactiveState(Unit)) coroutineContext[StatusListener]?.watchBackgroundProcess(reportTo) launch { @@ -127,25 +127,25 @@ interface CoroutineScopeHelpers : CoroutineScope { * Debounces listener notifications by [timeMs] milliseconds using this scope. State is always current. * @see DebounceReactive */ - fun Reactive.debounce(timeMs: Long): Reactive = DebounceReactive(this, this@CoroutineScopeHelpers, timeMs.milliseconds) + public fun Reactive.debounce(timeMs: Long): Reactive = DebounceReactive(this, this@CoroutineScopeHelpers, timeMs.milliseconds) /** * Debounces listener notifications by [duration] using this scope. State is always current. * @see DebounceReactive */ - fun Reactive.debounce(duration: Duration): Reactive = DebounceReactive(this, this@CoroutineScopeHelpers, duration) + public fun Reactive.debounce(duration: Duration): Reactive = DebounceReactive(this, this@CoroutineScopeHelpers, duration) /** * Debounces listener notifications by [timeMs] milliseconds using this scope. * @see DebounceListenable */ - fun Listenable.debounce(timeMs: Long): Listenable = DebounceListenable(this, this@CoroutineScopeHelpers, timeMs.milliseconds) + public fun Listenable.debounce(timeMs: Long): Listenable = DebounceListenable(this, this@CoroutineScopeHelpers, timeMs.milliseconds) /** * Debounces listener notifications by [duration] using this scope. * @see DebounceListenable */ - fun Listenable.debounce(duration: Duration): Listenable = DebounceListenable(this, this@CoroutineScopeHelpers, duration) + public fun Listenable.debounce(duration: Duration): Listenable = DebounceListenable(this, this@CoroutineScopeHelpers, duration) } @OptIn(ExperimentalStdlibApi::class) diff --git a/src/commonMain/kotlin/com/lightningkite/reactive/context/CoroutineTools.kt b/src/commonMain/kotlin/com/lightningkite/reactive/context/CoroutineTools.kt index d01f551..b05b1ba 100644 --- a/src/commonMain/kotlin/com/lightningkite/reactive/context/CoroutineTools.kt +++ b/src/commonMain/kotlin/com/lightningkite/reactive/context/CoroutineTools.kt @@ -18,7 +18,7 @@ import kotlin.coroutines.resumeWithException @OptIn(ExperimentalStdlibApi::class) -fun CoroutineScope.load(context: CoroutineContext = EmptyCoroutineContext, action: suspend () -> Unit): Job { +public fun CoroutineScope.load(context: CoroutineContext = EmptyCoroutineContext, action: suspend () -> Unit): Job { val state = RawReactive() val result = launch( context, @@ -35,8 +35,8 @@ fun CoroutineScope.load(context: CoroutineContext = EmptyCoroutineContext, actio return result } -class WaitGate(permit: Boolean = false) { - var permit: Boolean = permit +public class WaitGate(permit: Boolean = false) { + public var permit: Boolean = permit set(value) { field = value if (value) { @@ -46,18 +46,18 @@ class WaitGate(permit: Boolean = false) { continuations.clear() } } - fun permitOnce() { + public fun permitOnce() { permit = true permit = false } - val continuations = ArrayList>() - suspend fun await(): Unit { + private val continuations = ArrayList>() + public suspend fun await(): Unit { if (permit) return else return suspendCancellableCoroutine { continuations.add(it) } } - fun abandon() { + public fun abandon() { for (continuation in continuations) { continuation.resumeWithException(CancellationException("abandoned as requested")) } diff --git a/src/commonMain/kotlin/com/lightningkite/reactive/context/DependencyChangeListener.kt b/src/commonMain/kotlin/com/lightningkite/reactive/context/DependencyChangeListener.kt index 9d1c692..1a916f4 100644 --- a/src/commonMain/kotlin/com/lightningkite/reactive/context/DependencyChangeListener.kt +++ b/src/commonMain/kotlin/com/lightningkite/reactive/context/DependencyChangeListener.kt @@ -8,13 +8,13 @@ import com.lightningkite.reactive.core.addAndRunListener import kotlinx.coroutines.* import kotlin.coroutines.* -abstract class DependencyChangeListener : DependencyTracker(), CoroutineContext.Element { +public abstract class DependencyChangeListener : DependencyTracker(), CoroutineContext.Element { override val key: CoroutineContext.Key get() = Key - object Key : CoroutineContext.Key + public object Key : CoroutineContext.Key - abstract fun onDependencyChange() - open fun onDependencyNotReady() = onDependencyChange() + public abstract fun onDependencyChange() + public open fun onDependencyNotReady(): Unit = onDependencyChange() } private fun Continuation.resumeState(state: ReactiveState) { @@ -25,7 +25,7 @@ private fun Continuation.resumeState(state: ReactiveState) { ) } -suspend fun rerunOn(listenable: Listenable) { +public suspend fun rerunOn(listenable: Listenable) { currentCoroutineContext()[DependencyChangeListener.Key]?.let { if (it.existingDependency(listenable) == null) { it.registerDependency(listenable, listenable.addListener { it.onDependencyChange() }) @@ -33,11 +33,11 @@ suspend fun rerunOn(listenable: Listenable) { } } -suspend inline operator fun Reactive.invoke(): T = await() -suspend inline operator fun ReactiveValue.invoke(): T = await() -suspend inline fun Reactive.exception(): Exception? = state { it.exception } +public suspend inline operator fun Reactive.invoke(): T = await() +public suspend inline operator fun ReactiveValue.invoke(): T = await() +public suspend inline fun Reactive.exception(): Exception? = state { it.exception } -suspend fun Reactive.state(get: (ReactiveState) -> V): V { +public suspend fun Reactive.state(get: (ReactiveState) -> V): V { return currentCoroutineContext()[DependencyChangeListener.Key]?.let { // and the value is ready to go, just add the listener and proceed with the value. var last = state.let(get) @@ -56,7 +56,7 @@ suspend fun Reactive.state(get: (ReactiveState) -> V): V { } ?: state.let(get) } -suspend fun Reactive.state(): ReactiveState { +public suspend fun Reactive.state(): ReactiveState { return currentCoroutineContext()[DependencyChangeListener.Key]?.let { // and the value is ready to go, just add the listener and proceed with the value. var last = state @@ -75,7 +75,7 @@ suspend fun Reactive.state(): ReactiveState { } ?: state } -suspend fun ReactiveValue.await(): T { +public suspend fun ReactiveValue.await(): T { return currentCoroutineContext()[DependencyChangeListener.Key]?.let { // and the value is ready to go, just add the listener and proceed with the value. var last = value @@ -94,7 +94,7 @@ suspend fun ReactiveValue.await(): T { } ?: value } -suspend fun Reactive.await(): T { +public suspend fun Reactive.await(): T { return currentCoroutineContext()[DependencyChangeListener.Key]?.let { var cont: Continuation? = null if (it.existingDependency(this) == null) { @@ -138,7 +138,7 @@ suspend fun Reactive.await(): T { * source's own nature, as with a `Signal` - is keeping that value current. Only [ReactiveState.notActive] * and notReady require listening, and the subscription is released as soon as a value arrives. */ -suspend fun Reactive.awaitOnce(): T { +public suspend fun Reactive.awaitOnce(): T { val state = state @Suppress("DEPRECATION") return if (state.ready) state.get() diff --git a/src/commonMain/kotlin/com/lightningkite/reactive/context/DependencyTracker.kt b/src/commonMain/kotlin/com/lightningkite/reactive/context/DependencyTracker.kt index fb743aa..24a57be 100644 --- a/src/commonMain/kotlin/com/lightningkite/reactive/context/DependencyTracker.kt +++ b/src/commonMain/kotlin/com/lightningkite/reactive/context/DependencyTracker.kt @@ -1,6 +1,6 @@ package com.lightningkite.reactive.context -abstract class DependencyTracker { +public abstract class DependencyTracker { private val dependencies = ArrayList Unit>>() private val usedDependencies = ArrayList() @@ -19,7 +19,7 @@ abstract class DependencyTracker { * so the fast-path alignment above holds even when a call site re-reads an earlier dependency. */ @Suppress("UNCHECKED_CAST") - fun existingDependency(listenable: T): T? { + public fun existingDependency(listenable: T): T? { val index = usedDependencies.size if (index < dependencies.size) { val maybe = dependencies[index].first @@ -33,11 +33,11 @@ abstract class DependencyTracker { return found } - fun registerDependency(any: Any, remove: () -> Unit) { + public fun registerDependency(any: Any, remove: () -> Unit) { this.dependencies += any to remove } - open fun cancel() { + public open fun cancel() { dependencies.forEach { it.second() } dependencies.clear() } diff --git a/src/commonMain/kotlin/com/lightningkite/reactive/context/ReactiveContext.kt b/src/commonMain/kotlin/com/lightningkite/reactive/context/ReactiveContext.kt index 34a8eae..ad28b47 100644 --- a/src/commonMain/kotlin/com/lightningkite/reactive/context/ReactiveContext.kt +++ b/src/commonMain/kotlin/com/lightningkite/reactive/context/ReactiveContext.kt @@ -32,7 +32,7 @@ import kotlin.coroutines.CoroutineContext * } * ``` */ -typealias ReactiveContext = TypedReactiveContext<*> +public typealias ReactiveContext = TypedReactiveContext<*> /** * Implements the core logic for a single reactive calculation, managing its dependencies and lifecycle. @@ -154,20 +154,20 @@ typealias ReactiveContext = TypedReactiveContext<*> * @property reportTo The underlying [RawReactive] to report state updates to. * @property action The calculation logic to execute in this context. */ -class TypedReactiveContext( - val scope: CoroutineScope, - val useLastWhileLoading: Boolean = false, - val reentrancyLimit: Int = 0, +public class TypedReactiveContext( + public val scope: CoroutineScope, + public val useLastWhileLoading: Boolean = false, + public val reentrancyLimit: Int = 0, private val reportTo: RawReactive = RawReactive(), - val action: TypedReactiveContext.() -> T + public val action: TypedReactiveContext.() -> T ) : DependencyChangeListener(), ReactiveCoroutineScope, Reactive by reportTo { - companion object + public companion object {} /** * Whether this context is currently active and tracking dependencies. * Set to false when [cancel] is called. */ - var active = false + public var active: Boolean = false private set /** @@ -180,7 +180,7 @@ class TypedReactiveContext( * arrives through this path while the calculation is still running is indistinguishable from * the calculation triggering itself, and so spends [reentrancyLimit]. */ - val rerun: () -> Unit = ::startCalculation + public val rerun: () -> Unit = ::startCalculation /** * The current job for this calculation run. @@ -229,7 +229,7 @@ class TypedReactiveContext( * than recursing until the stack overflows, or livelocking under a dispatching scheduler. The * default limit of zero forbids self-triggering outright. */ - fun startCalculation() { + public fun startCalculation() { active = true if (queued) return // Prevent duplicate queuing; the pending run will see the new state queued = true @@ -324,7 +324,7 @@ class TypedReactiveContext( /** * Starts using this [ResourceUse] and tracks it as a dependency in future loops. * */ - fun use(resourceUse: ResourceUse) { + public fun use(resourceUse: ResourceUse) { if (existingDependency(resourceUse) != null) return registerDependency(resourceUse, resourceUse.beginUse()) } @@ -343,7 +343,7 @@ class TypedReactiveContext( * } * ``` */ - fun rerunOn(listenable: Listenable) { + public fun rerunOn(listenable: Listenable) { if (existingDependency(listenable) != null) return registerDependency(listenable, listenable.addListener(rerun)) } @@ -383,7 +383,7 @@ class TypedReactiveContext( * @return The current value of this reactive * @throws ReactiveLoading if the value is not ready */ - operator fun Reactive.invoke(): R { + public operator fun Reactive.invoke(): R { if (existingDependency(this) == null) { registerDependency(this, addListener(rerun)) } @@ -407,7 +407,7 @@ class TypedReactiveContext( * @return The non-null value * @throws ReactiveLoading if the value is null or not ready */ - fun Reactive.awaitNotNull(): R { + public fun Reactive.awaitNotNull(): R { if (existingDependency(this) == null) { registerDependency(this, addListener(rerun)) } @@ -433,7 +433,7 @@ class TypedReactiveContext( * * @return The current [ReactiveState] */ - fun Reactive.state(): ReactiveState { + public fun Reactive.state(): ReactiveState { if (existingDependency(this) == null) { registerDependency(this, addListener(rerun)) } @@ -457,7 +457,7 @@ class TypedReactiveContext( * @param get Function to extract a value from the [ReactiveState] * @return The transformed value */ - fun Reactive.state(get: (ReactiveState) -> V): V { + public fun Reactive.state(get: (ReactiveState) -> V): V { var current: V = state.let(get) if (existingDependency(this) == null) { registerDependency(this, addListener { @@ -501,7 +501,7 @@ class TypedReactiveContext( * @return The value once it's ready * @throws ReactiveLoading if the value is not ready yet */ - fun Reactive.once(): T { + public fun Reactive.once(): T { val key = existingDependency(Once(this)) ?: Once(this).also { key -> registerDependency(key, addListener { if (!key.have) rerun() }) } @@ -512,19 +512,19 @@ class TypedReactiveContext( // Hack: fixes compiler weirdness around lambdas with 'this' @Suppress("NOTHING_TO_INLINE") - inline operator fun (ReactiveContext.() -> T).invoke(): T = invoke(this@TypedReactiveContext) + public inline operator fun (ReactiveContext.() -> T).invoke(): T = invoke(this@TypedReactiveContext) @Suppress("NOTHING_TO_INLINE") - inline operator fun (ReactiveContext.(A) -> T).invoke(a: A): T = invoke(this@TypedReactiveContext, a) + public inline operator fun (ReactiveContext.(A) -> T).invoke(a: A): T = invoke(this@TypedReactiveContext, a) @Suppress("NOTHING_TO_INLINE") - inline operator fun (ReactiveContext.(A, B) -> T).invoke(a: A, b: B): T = invoke(this@TypedReactiveContext, a, b) + public inline operator fun (ReactiveContext.(A, B) -> T).invoke(a: A, b: B): T = invoke(this@TypedReactiveContext, a, b) @Deprecated("Just use the invoke operator", ReplaceWith("this()")) - fun Reactive.await(): T = invoke() + public fun Reactive.await(): T = invoke() @Deprecated("Just use the once function", ReplaceWith("this.once()")) - fun Reactive.awaitOnce(): T = once() + public fun Reactive.awaitOnce(): T = once() // Suspending calculations @@ -568,7 +568,7 @@ class TypedReactiveContext( * @return The result of the calculation once complete * @throws ReactiveLoading if the calculation is not yet complete */ - fun async(identity: String, vararg dependencies: Any?, action: suspend () -> T): T { + public fun async(identity: String, vararg dependencies: Any?, action: suspend () -> T): T { // action::class distinguishes call sites: two different `async(...) { }` call sites compile // to distinct anonymous classes, so folding it into the key stops two call sites that // happen to pass identical dependencies from colliding on the same cached calculation. @@ -614,7 +614,7 @@ class TypedReactiveContext( * @return The deferred value once available * @throws ReactiveLoading if the deferred is not yet complete */ - operator fun Deferred.invoke(): T { + public operator fun Deferred.invoke(): T { val calc = SuspendCalculation(this) // Reuse existing calculation if already running @@ -671,7 +671,7 @@ class TypedReactiveContext( * @return The latest emitted value * @throws ReactiveLoading if no value has been emitted yet (except for StateFlow) */ - operator fun Flow.invoke(): T { + public operator fun Flow.invoke(): T { val new = FlowLoader(this) val existing = existingDependency(new) @@ -775,7 +775,7 @@ class TypedReactiveContext( * @see TypedReactiveContext for implementation details * @see reactiveSuspending for suspending calculations */ -fun CoroutineScope.reactive(reentrancyLimit: Int = 0, action: ReactiveContext.() -> T): TypedReactiveContext { +public fun CoroutineScope.reactive(reentrancyLimit: Int = 0, action: ReactiveContext.() -> T): TypedReactiveContext { val trc = TypedReactiveContext(this, reentrancyLimit = reentrancyLimit, action = action) trc.startCalculation() coroutineContext[StatusListener]?.watchBackgroundProcess(trc) @@ -801,7 +801,7 @@ fun CoroutineScope.reactive(reentrancyLimit: Int = 0, action: ReactiveContex * @param action The calculation logic to run reactively. * @return A [TypedReactiveContext] managing the calculation and its dependencies. */ -inline fun CoroutineScope.reactive(crossinline onLoad: () -> Unit, crossinline action: ReactiveContext.() -> Unit): TypedReactiveContext { +public inline fun CoroutineScope.reactive(crossinline onLoad: () -> Unit, crossinline action: ReactiveContext.() -> Unit): TypedReactiveContext { var wasLoadingLastTime = false return reactive { try { @@ -827,7 +827,7 @@ inline fun CoroutineScope.reactive(crossinline onLoad: () -> Unit, crossinline a * @param action The calculation logic to run reactively. */ @Deprecated("renamed to 'reactive'", ReplaceWith("this.reactive(action)")) -fun CoroutineScope.reactiveScope(action: ReactiveContext.() -> Unit): ReactiveContext = reactive(action = action) +public fun CoroutineScope.reactiveScope(action: ReactiveContext.() -> Unit): ReactiveContext = reactive(action = action) /** * Creates a [ReactiveContext] in which to run the provided [action] reactively, discarding the result, with support for loading state. @@ -839,17 +839,17 @@ fun CoroutineScope.reactiveScope(action: ReactiveContext.() -> Unit): ReactiveCo * @param action The calculation logic to run reactively. */ @Deprecated("renamed to 'reactive'", ReplaceWith("this.reactive(onLoad, action)")) -inline fun CoroutineScope.reactiveScope(crossinline onLoad: () -> Unit, crossinline action: ReactiveContext.() -> Unit): ReactiveContext = reactive(onLoad = onLoad, action = action) +public inline fun CoroutineScope.reactiveScope(crossinline onLoad: () -> Unit, crossinline action: ReactiveContext.() -> Unit): ReactiveContext = reactive(onLoad = onLoad, action = action) @InternalReactiveApi -object ReactiveLoading : Throwable() +public object ReactiveLoading : Throwable() /** * Thrown when a reactive calculation triggers its own re-execution, typically by writing to a * signal it also reads inside the same [reactive] block. This would otherwise recurse until the * stack overflows (or livelock under a dispatching scheduler), so it is surfaced as a clear error. */ -class ReactiveReentrancyException(context: ReactiveContext, reentrancyLimit: Int) : IllegalStateException( +public class ReactiveReentrancyException(context: ReactiveContext, reentrancyLimit: Int) : IllegalStateException( if (reentrancyLimit <= 0) "A reactive calculation triggered its own re-execution ($context). This usually means the " + "calculation wrote to a signal it also reads. Break the cycle so the calculation " + diff --git a/src/commonMain/kotlin/com/lightningkite/reactive/context/ReactiveContextSuspending.kt b/src/commonMain/kotlin/com/lightningkite/reactive/context/ReactiveContextSuspending.kt index a6e42ab..be9b0a0 100644 --- a/src/commonMain/kotlin/com/lightningkite/reactive/context/ReactiveContextSuspending.kt +++ b/src/commonMain/kotlin/com/lightningkite/reactive/context/ReactiveContextSuspending.kt @@ -92,11 +92,11 @@ import kotlin.coroutines.CoroutineContext * @property reportTo The underlying [RawReactive] to report state updates to. * @property action The suspending calculation logic to execute in this context. */ -class ReactiveContextSuspending( - val scope: CoroutineScope, - val useLastWhileLoading: Boolean = false, +public class ReactiveContextSuspending( + public val scope: CoroutineScope, + public val useLastWhileLoading: Boolean = false, private val reportTo: RawReactive = RawReactive(), - val action: suspend ReactiveCoroutineScope.() -> T, + public val action: suspend ReactiveCoroutineScope.() -> T, ) : DependencyChangeListener(), ReactiveCoroutineScope, Reactive by reportTo { /** * The job for the current calculation run's coroutine. @@ -108,7 +108,7 @@ class ReactiveContextSuspending( * Whether this context is currently active and tracking dependencies. * Set to false when [cancel] is called. */ - var active = false + public var active: Boolean = false private set /** @@ -161,7 +161,7 @@ class ReactiveContextSuspending( * The calculation may complete synchronously (if already on correct dispatcher and no suspension points) * or asynchronously. If [useLastWhileLoading] is false, the state is set to notReady during async execution. */ - fun startCalculation() { + public fun startCalculation() { active = true lastLoopJob?.cancel() // Cancel previous calculation if still running @@ -310,7 +310,7 @@ class ReactiveContextSuspending( * @see ReactiveContextSuspending for implementation details * @see reactive for non-suspending calculations */ -fun CoroutineScope.reactiveSuspending(action: suspend ReactiveCoroutineScope.() -> Unit) = +public fun CoroutineScope.reactiveSuspending(action: suspend ReactiveCoroutineScope.() -> Unit): ReactiveContextSuspending = ReactiveContextSuspending(this, action = action).also { it.startCalculation() coroutineContext[StatusListener.Key]?.watchBackgroundProcess(it) @@ -326,7 +326,7 @@ fun CoroutineScope.reactiveSuspending(action: suspend ReactiveCoroutineScope.() * @param action The suspending calculation logic to run reactively. * @return A [ReactiveContextSuspending] managing the calculation and its dependencies. */ -inline fun CoroutineScope.reactiveSuspending(crossinline onLoad: () -> Unit, noinline action: suspend ReactiveCoroutineScope.() -> Unit): ReactiveContextSuspending { +public inline fun CoroutineScope.reactiveSuspending(crossinline onLoad: () -> Unit, noinline action: suspend ReactiveCoroutineScope.() -> Unit): ReactiveContextSuspending { return reactiveSuspending(action = action).also { it.addListener { if (!it.state.ready) onLoad() }.let(::onRemove) } diff --git a/src/commonMain/kotlin/com/lightningkite/reactive/core/AppScope.kt b/src/commonMain/kotlin/com/lightningkite/reactive/core/AppScope.kt index 4f88acc..92c16dc 100644 --- a/src/commonMain/kotlin/com/lightningkite/reactive/core/AppScope.kt +++ b/src/commonMain/kotlin/com/lightningkite/reactive/core/AppScope.kt @@ -1,12 +1,13 @@ package com.lightningkite.reactive.core +import kotlinx.coroutines.CompletableJob import kotlinx.coroutines.CoroutineExceptionHandler import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.SupervisorJob -val AppJob = SupervisorJob() +public val AppJob: CompletableJob = SupervisorJob() -val AppScope = CoroutineScope(AppJob + CoroutineExceptionHandler { coroutineContext, throwable -> +public val AppScope: CoroutineScope = CoroutineScope(AppJob + CoroutineExceptionHandler { coroutineContext, throwable -> Reactive.reportException(throwable) } + Dispatchers.Main.immediate) \ No newline at end of file diff --git a/src/commonMain/kotlin/com/lightningkite/reactive/core/Draft.kt b/src/commonMain/kotlin/com/lightningkite/reactive/core/Draft.kt index 8d759d8..b50936c 100644 --- a/src/commonMain/kotlin/com/lightningkite/reactive/core/Draft.kt +++ b/src/commonMain/kotlin/com/lightningkite/reactive/core/Draft.kt @@ -24,28 +24,28 @@ import com.lightningkite.reactive.context.awaitOnce * draft.cancel() // discards changes and resets the draft. draft now reads '42' again. * ``` */ -interface Draft : ReactiveWithMutableValue { +public interface Draft : ReactiveWithMutableValue { /** * The current saved value that this [Draft] is buffering. * * NOTE: Manually setting values for [published] will not by-default update values in the draft buffer. * */ - val published: MutableReactive + public val published: MutableReactive /** * Saves all changes made to this [Draft] to the published [MutableReactive] * */ - suspend fun publish(): T + public suspend fun publish(): T /** * Discards all changes made to this [Draft] and reverts back to the [published] state * */ - fun cancel() + public fun cancel() /** * Reads `true` if there are any differences between the [published] value and the value stored in the draft buffer. * */ - val changesMade: Reactive + public val changesMade: Reactive } private class BaseDraft private constructor( @@ -67,14 +67,14 @@ private class BaseDraft private constructor( /** * Creates a [Draft] using the specified [MutableReactive] as the published value. * */ -fun Draft(published: MutableReactive): Draft = BaseDraft(published) +public fun Draft(published: MutableReactive): Draft = BaseDraft(published) /** * Creates a [Draft] where the published value is the provided [initialValue] * */ -fun Draft(initialValue: T): Draft = BaseDraft(Signal(initialValue)) +public fun Draft(initialValue: T): Draft = BaseDraft(Signal(initialValue)) /** * Creates a [Draft] where the published value is calculated based off the provided [initialValue] calculation. * */ -fun Draft(initialValue: ReactiveContext.() -> T): Draft = BaseDraft(MutableRemember(useLastWhileLoading = true, initialValue = initialValue)) \ No newline at end of file +public fun Draft(initialValue: ReactiveContext.() -> T): Draft = BaseDraft(MutableRemember(useLastWhileLoading = true, initialValue = initialValue)) \ No newline at end of file diff --git a/src/commonMain/kotlin/com/lightningkite/reactive/core/InternalReactiveApi.kt b/src/commonMain/kotlin/com/lightningkite/reactive/core/InternalReactiveApi.kt index 7676db1..207dc9a 100644 --- a/src/commonMain/kotlin/com/lightningkite/reactive/core/InternalReactiveApi.kt +++ b/src/commonMain/kotlin/com/lightningkite/reactive/core/InternalReactiveApi.kt @@ -5,4 +5,4 @@ package com.lightningkite.reactive.core level = RequiresOptIn.Level.WARNING, message = "This may change, use it at your own risk" ) -annotation class InternalReactiveApi \ No newline at end of file +public annotation class InternalReactiveApi \ No newline at end of file diff --git a/src/commonMain/kotlin/com/lightningkite/reactive/core/MutableRemember.kt b/src/commonMain/kotlin/com/lightningkite/reactive/core/MutableRemember.kt index f570fb6..6ea3393 100644 --- a/src/commonMain/kotlin/com/lightningkite/reactive/core/MutableRemember.kt +++ b/src/commonMain/kotlin/com/lightningkite/reactive/core/MutableRemember.kt @@ -32,7 +32,7 @@ import kotlin.time.Duration * * @see remember */ -fun mutableRemember( +public fun mutableRemember( useLastWhileLoading: Boolean = false, coroutineContext: CoroutineContext = Dispatchers.Unconfined, initialValue: ReactiveContext.() -> T @@ -57,14 +57,14 @@ fun mutableRemember( * * @see Remember */ -class MutableRemember( +public class MutableRemember( private val stopListeningWhenOverridden: Boolean = true, private val useLastWhileLoading: Boolean = false, coroutineContext: CoroutineContext = Dispatchers.Unconfined, deactivationDelay: Duration? = null, initialValue: ReactiveContext.() -> T ): ReactiveWithMutableValue, BaseReactive() { - var overridden: Boolean = false + public var overridden: Boolean = false private set private val remember = Remember(coroutineContext, useLastWhileLoading, deactivationDelay, action = initialValue) @@ -117,7 +117,7 @@ class MutableRemember( * - This does not forcefully notify listeners. If the value calculated after resetting is the same as the previously set value, * then no listeners will be notified. */ - fun reset() { + public fun reset() { if (overridden) { overridden = false if (stopListeningWhenOverridden) startListening() diff --git a/src/commonMain/kotlin/com/lightningkite/reactive/core/MutableRememberSuspending.kt b/src/commonMain/kotlin/com/lightningkite/reactive/core/MutableRememberSuspending.kt index d837487..90c7e4b 100644 --- a/src/commonMain/kotlin/com/lightningkite/reactive/core/MutableRememberSuspending.kt +++ b/src/commonMain/kotlin/com/lightningkite/reactive/core/MutableRememberSuspending.kt @@ -33,7 +33,7 @@ import kotlin.time.Duration * @see rememberSuspending * @see mutableRemember */ -fun mutableRememberSuspending( +public fun mutableRememberSuspending( useLastWhileLoading: Boolean = false, coroutineContext: CoroutineContext = Dispatchers.Unconfined, initialValue: suspend CoroutineScope.() -> T @@ -58,14 +58,14 @@ fun mutableRememberSuspending( * * @see [MutableRemember] */ -class MutableRememberSuspending( +public class MutableRememberSuspending( private val stopListeningWhenOverridden: Boolean = true, private val useLastWhileLoading: Boolean = false, coroutineContext: CoroutineContext = Dispatchers.Unconfined, deactivationDelay: Duration? = null, initialValue: suspend CoroutineScope.() -> T ) : ReactiveWithMutableValue, BaseReactive() { - var overridden: Boolean = false + public var overridden: Boolean = false private set private val remember = RememberSuspending(coroutineContext, useLastWhileLoading, deactivationDelay, initialValue) @@ -123,7 +123,7 @@ class MutableRememberSuspending( * - This does not forcefully notify listeners. If the value calculated after resetting is the same as the previously set value, * then no listeners will be notified. */ - fun reset() { + public fun reset() { if (overridden) { overridden = false if (stopListeningWhenOverridden) startListening() diff --git a/src/commonMain/kotlin/com/lightningkite/reactive/core/ReactiveMutableList.kt b/src/commonMain/kotlin/com/lightningkite/reactive/core/ReactiveMutableList.kt index acd630a..7259f40 100644 --- a/src/commonMain/kotlin/com/lightningkite/reactive/core/ReactiveMutableList.kt +++ b/src/commonMain/kotlin/com/lightningkite/reactive/core/ReactiveMutableList.kt @@ -6,10 +6,10 @@ import com.lightningkite.reactive.lensing.lens /** * A wrapper around [ArrayList] that signals its listeners whenever it is mutated * */ -class ReactiveMutableList(private val list: ArrayList): MutableList by list, MutableReactiveValue>, BaseListenable() { - constructor() : this(ArrayList()) - constructor(items: List) : this(ArrayList(items)) - constructor(vararg startingItems: T) : this(ArrayList(startingItems.asList())) +public class ReactiveMutableList(private val list: ArrayList): MutableList by list, MutableReactiveValue>, BaseListenable() { + public constructor() : this(ArrayList()) + public constructor(items: List) : this(ArrayList(items)) + public constructor(vararg startingItems: T) : this(ArrayList(startingItems.asList())) override var value: List get() = list @@ -33,11 +33,11 @@ class ReactiveMutableList(private val list: ArrayList): MutableList by override fun removeAll(elements: Collection): Boolean = signalChange { removeAll(elements) } override fun addAll(elements: Collection): Boolean = signalChange { addAll(elements) } override fun addAll(index: Int, elements: Collection): Boolean = signalChange { addAll(index, elements) } - override fun add(index: Int, element: T) = signal { add(index, element) } + override fun add(index: Int, element: T): Unit = signal { add(index, element) } override fun add(element: T): Boolean = signal { add(element) } override fun remove(element: T): Boolean = signalChange { remove(element) } - fun reactiveContains(element: T) = object : MutableReactiveValue { + public fun reactiveContains(element: T): MutableReactiveValue = object : MutableReactiveValue { private val lens = this@ReactiveMutableList.lens { element in it } override fun addListener(listener: () -> Unit): Release = lens.addListener(listener) diff --git a/src/commonMain/kotlin/com/lightningkite/reactive/core/ReactiveMutableMap.kt b/src/commonMain/kotlin/com/lightningkite/reactive/core/ReactiveMutableMap.kt index a57014c..0c4d165 100644 --- a/src/commonMain/kotlin/com/lightningkite/reactive/core/ReactiveMutableMap.kt +++ b/src/commonMain/kotlin/com/lightningkite/reactive/core/ReactiveMutableMap.kt @@ -5,8 +5,8 @@ import com.lightningkite.reactive.lensing.lens /** * A wrapper around [HashMap] that signals its listeners whenever it is mutated * */ -class ReactiveMutableMap(private val map: HashMap): MutableMap by map, MutableReactiveValue>, BaseListenable() { - constructor() : this(HashMap()) +public class ReactiveMutableMap(private val map: HashMap): MutableMap by map, MutableReactiveValue>, BaseListenable() { + public constructor() : this(HashMap()) override var value: Map get() = map @@ -18,26 +18,26 @@ class ReactiveMutableMap(private val map: HashMap): MutableMap } } - val reactiveEntries: ReactiveValue>> = + public val reactiveEntries: ReactiveValue>> = object : ReactiveValue>>, Listenable by this { override val value: Set> get() = map.entries } - val reactiveKeys: ReactiveValue> = + public val reactiveKeys: ReactiveValue> = object : ReactiveValue>, Listenable by this { override val value: Set get() = map.keys } - val reactiveValues: ReactiveValue> = + public val reactiveValues: ReactiveValue> = object : ReactiveValue>, Listenable by this { override val value: Collection get() = map.values } private inline fun signal(operation: MutableMap.()->T) = map.operation().also { invokeAllListeners() } - override fun clear() = signal { clear() } + override fun clear(): Unit = signal { clear() } override fun remove(key: K): V? = map.remove(key).also { if (it != null) invokeAllListeners() } - override fun putAll(from: Map) = signal { putAll(from) } + override fun putAll(from: Map): Unit = signal { putAll(from) } override fun put(key: K, value: V): V? = signal { put(key, value) } private inner class Element(private val key: K) : MutableReactiveValue { @@ -53,5 +53,5 @@ class ReactiveMutableMap(private val map: HashMap): MutableMap } } - fun getReactive(key: K): MutableReactiveValue = Element(key) + public fun getReactive(key: K): MutableReactiveValue = Element(key) } \ No newline at end of file diff --git a/src/commonMain/kotlin/com/lightningkite/reactive/core/ReactiveMutableSet.kt b/src/commonMain/kotlin/com/lightningkite/reactive/core/ReactiveMutableSet.kt index b4b2195..2616d79 100644 --- a/src/commonMain/kotlin/com/lightningkite/reactive/core/ReactiveMutableSet.kt +++ b/src/commonMain/kotlin/com/lightningkite/reactive/core/ReactiveMutableSet.kt @@ -6,10 +6,10 @@ import com.lightningkite.reactive.lensing.lens /** * A wrapper around [LinkedHashSet] that signals its listeners whenever it is mutated * */ -class ReactiveMutableSet(private val hashSet: LinkedHashSet): MutableSet by hashSet, MutableReactiveValue>, BaseListenable() { - constructor() : this(LinkedHashSet()) - constructor(items: Set) : this(LinkedHashSet(items)) - constructor(vararg startingItems: T) : this(LinkedHashSet(startingItems.toList())) +public class ReactiveMutableSet(private val hashSet: LinkedHashSet): MutableSet by hashSet, MutableReactiveValue>, BaseListenable() { + public constructor() : this(LinkedHashSet()) + public constructor(items: Set) : this(LinkedHashSet(items)) + public constructor(vararg startingItems: T) : this(LinkedHashSet(startingItems.toList())) override var value: Set get() = hashSet @@ -34,7 +34,7 @@ class ReactiveMutableSet(private val hashSet: LinkedHashSet): MutableSet { + public fun reactiveContains(element: T): MutableReactiveValue = object : MutableReactiveValue { private val lens = this@ReactiveMutableSet.lens { element in it } override fun addListener(listener: () -> Unit): Release = lens.addListener(listener) diff --git a/src/commonMain/kotlin/com/lightningkite/reactive/core/ReactiveState.kt b/src/commonMain/kotlin/com/lightningkite/reactive/core/ReactiveState.kt index da8ad4a..fd8bd8a 100644 --- a/src/commonMain/kotlin/com/lightningkite/reactive/core/ReactiveState.kt +++ b/src/commonMain/kotlin/com/lightningkite/reactive/core/ReactiveState.kt @@ -28,49 +28,49 @@ import kotlin.jvm.JvmInline */ @JvmInline @OptIn(InternalReactiveApi::class) -value class ReactiveState(val raw: T) { - inline val ready: Boolean get() = raw != InternalReactiveNotReady && raw != InternalReactiveNotActive - inline val success: Boolean get() = ready && raw !is InternalReactiveThrownException +public value class ReactiveState(public val raw: T) { + public inline val ready: Boolean get() = raw != InternalReactiveNotReady && raw != InternalReactiveNotActive + public inline val success: Boolean get() = ready && raw !is InternalReactiveThrownException /** True when nothing is maintaining this value; see the [ReactiveState] docs. */ - inline val notActive: Boolean get() = raw is InternalReactiveNotActive + public inline val notActive: Boolean get() = raw is InternalReactiveNotActive - inline fun onSuccess(action: (T)->R): R? = handle( + public inline fun onSuccess(action: (T)->R): R? = handle( success = { action(it) }, exception = { null }, notReady = { null } ) - inline val exception: Exception? get() = (raw as? InternalReactiveThrownException)?.exception + public inline val exception: Exception? get() = (raw as? InternalReactiveThrownException)?.exception @Deprecated("Only use this if you are *Absolutely Sure* that there is a value ready to retrieve. Otherwise, use `handle`.") - fun get(): T = handle( + public fun get(): T = handle( success = { it }, exception = { throw it }, notReady = { throw NotReadyException() }, notActive = { throw NotActiveException() } ) - fun getOrNull(): T? = handle( + public fun getOrNull(): T? = handle( success = { it }, exception = { null }, notReady = { null } ) - companion object Companion { + public companion object Companion { @Suppress("UNCHECKED_CAST") - val notReady: ReactiveState = ReactiveState(InternalReactiveNotReady) as ReactiveState + public val notReady: ReactiveState = ReactiveState(InternalReactiveNotReady) as ReactiveState /** No value, because nothing is maintaining one; see the [ReactiveState] docs. */ @Suppress("UNCHECKED_CAST") - val notActive: ReactiveState = ReactiveState(InternalReactiveNotActive) as ReactiveState + public val notActive: ReactiveState = ReactiveState(InternalReactiveNotActive) as ReactiveState @Suppress("UNCHECKED_CAST") - fun exception(exception: Exception) = (if(exception is CancellationException) notReady else ReactiveState(InternalReactiveThrownException(exception))) as ReactiveState + public fun exception(exception: Exception): ReactiveState = (if(exception is CancellationException) notReady else ReactiveState(InternalReactiveThrownException(exception))) as ReactiveState @Suppress("UNCHECKED_CAST") - fun wrap(value: T) = ReactiveState(InternalReactiveWrapper(value)) as ReactiveState + public fun wrap(value: T): ReactiveState = ReactiveState(InternalReactiveWrapper(value)) as ReactiveState } @Suppress("UNCHECKED_CAST") - inline fun map(mapper: (T)->B): ReactiveState { + public inline fun map(mapper: (T)->B): ReactiveState { // notActive propagates like the other valueless states: a value derived from a source // nobody is maintaining is equally unmaintained. if(raw is InternalReactiveNotReady || raw is InternalReactiveNotActive || raw is InternalReactiveThrownException) return this as ReactiveState @@ -90,14 +90,14 @@ value class ReactiveState(val raw: T) { * value" and "there is no value yet" are the same thing to code that only wants to display or * wait for one. Use the four-argument overload to do something better, such as subscribing. */ - inline fun handle( + public inline fun handle( success: (T)->R, exception: (Exception)->R, notReady: ()->R ): R = handle(success, exception, notReady, notReady) @Suppress("UNCHECKED_CAST") - inline fun handle( + public inline fun handle( success: (T)->R, exception: (Exception)->R, notReady: ()->R, @@ -112,7 +112,7 @@ value class ReactiveState(val raw: T) { } } - fun asResult(): Result = handle( + public fun asResult(): Result = handle( success = { Result.success(it) }, exception = { Result.failure(it) }, notReady = { Result.failure(NotReadyException()) }, @@ -128,24 +128,24 @@ value class ReactiveState(val raw: T) { } } @InternalReactiveApi -data class InternalReactiveWrapper(val other: T) +public data class InternalReactiveWrapper(val other: T) @InternalReactiveApi -data class InternalReactiveThrownException(val exception: Exception) +public data class InternalReactiveThrownException(val exception: Exception) @InternalReactiveApi -object InternalReactiveNotReady +public object InternalReactiveNotReady @InternalReactiveApi -object InternalReactiveNotActive +public object InternalReactiveNotActive -open class NotReadyException(message: String? = null) : IllegalStateException(message) +public open class NotReadyException(message: String? = null) : IllegalStateException(message) /** * Thrown when reading a value from a reactive that nothing is listening to, and which therefore * has no value to give. Subscribe to it first, or use `awaitOnce`, which subscribes for as long * as it takes to obtain a value. */ -class NotActiveException(message: String = "Nothing is listening to this reactive value, so it has no value to report. Subscribe to it, or use awaitOnce.") : NotReadyException(message) +public class NotActiveException(message: String = "Nothing is listening to this reactive value, so it has no value to report. Subscribe to it, or use awaitOnce.") : NotReadyException(message) -inline fun reactiveState(action: () -> T): ReactiveState { +public inline fun reactiveState(action: () -> T): ReactiveState { @OptIn(InternalReactiveApi::class) return try { ReactiveState(action()) @@ -161,7 +161,7 @@ inline fun reactiveState(action: () -> T): ReactiveState { } } -fun Result.toReactiveState(): ReactiveState { +public fun Result.toReactiveState(): ReactiveState { @Suppress("UNCHECKED_CAST") return if(this.isFailure) ReactiveState.exception(this.exceptionOrNull() as Exception) else ReactiveState.wrap(this.getOrNull() as T) diff --git a/src/commonMain/kotlin/com/lightningkite/reactive/core/Remember.kt b/src/commonMain/kotlin/com/lightningkite/reactive/core/Remember.kt index d0134a5..6044c3a 100644 --- a/src/commonMain/kotlin/com/lightningkite/reactive/core/Remember.kt +++ b/src/commonMain/kotlin/com/lightningkite/reactive/core/Remember.kt @@ -39,7 +39,7 @@ import kotlin.time.Duration * b.value = 2 // prints "sum: 3" * ``` */ -fun remember( +public fun remember( coroutineContext: CoroutineContext = Dispatchers.Unconfined, useLastWhileLoading: Boolean = false, deactivationDelay: Duration? = null, @@ -74,11 +74,11 @@ fun remember( * Listeners can be added to be notified when the value changes. The calculation is protected against * cancellation exceptions, and any other exceptions are reported via [Reactive.reportException]. */ -class Remember( - val incomingCoroutineContext: CoroutineContext = Dispatchers.Unconfined, +public class Remember( + public val incomingCoroutineContext: CoroutineContext = Dispatchers.Unconfined, private val useLastWhileLoading: Boolean = false, private val deactivationDelay: Duration? = null, - private val reentrancyLimit: Int = 0, + reentrancyLimit: Int = 0, private val action: ReactiveContext.() -> T, ) : Reactive, CoroutineScope, BaseListenable() { @@ -96,7 +96,7 @@ class Remember( // and TypedReactiveContext.init's `scope.onRemove { cancel() }` then attaches an // invokeOnCompletion handler to the app-lifetime Job that never fires — leaking every // Remember/shared reactive graph forever. Matches RememberSuspending's ordering. - override val coroutineContext get() = restOfContext + job + override val coroutineContext: CoroutineContext get() = restOfContext + job // Starts notActive rather than notReady: nothing is listening yet, so there is no value to // be had, as opposed to one that is on its way. diff --git a/src/commonMain/kotlin/com/lightningkite/reactive/core/RememberSuspending.kt b/src/commonMain/kotlin/com/lightningkite/reactive/core/RememberSuspending.kt index 0d0e43b..f1e1368 100644 --- a/src/commonMain/kotlin/com/lightningkite/reactive/core/RememberSuspending.kt +++ b/src/commonMain/kotlin/com/lightningkite/reactive/core/RememberSuspending.kt @@ -38,7 +38,7 @@ import kotlin.time.Duration * * @see [remember] */ -fun rememberSuspending( +public fun rememberSuspending( coroutineContext: CoroutineContext = Dispatchers.Unconfined, useLastWhileLoading: Boolean = false, deactivationDelay: Duration? = null, @@ -72,9 +72,9 @@ fun rememberSuspending( * * @see [Remember] */ -class RememberSuspending( - val incomingCoroutineContext: CoroutineContext = Dispatchers.Unconfined, - private val useLastWhileLoading: Boolean = false, +public class RememberSuspending( + public val incomingCoroutineContext: CoroutineContext = Dispatchers.Unconfined, + useLastWhileLoading: Boolean = false, private val deactivationDelay: Duration? = null, private val action: suspend ReactiveCoroutineScope.() -> T, ) : Reactive, CoroutineScope, BaseListenable() { diff --git a/src/commonMain/kotlin/com/lightningkite/reactive/core/abstracts.kt b/src/commonMain/kotlin/com/lightningkite/reactive/core/abstracts.kt index e02a11b..e8d0668 100644 --- a/src/commonMain/kotlin/com/lightningkite/reactive/core/abstracts.kt +++ b/src/commonMain/kotlin/com/lightningkite/reactive/core/abstracts.kt @@ -22,7 +22,7 @@ import kotlin.coroutines.cancellation.CancellationException * } * ``` */ -abstract class BaseListenable : Listenable { +public abstract class BaseListenable : Listenable { /** * Called when the first listener is added. * Override to start calculations, resource usage, or subscriptions. @@ -41,7 +41,7 @@ abstract class BaseListenable : Listenable { * Number of currently-registered listeners. Exposed for tests that need to assert * that a listenable is not leaking subscriptions. */ - val listenerCount: Int get() = listeners.size + public val listenerCount: Int get() = listeners.size override fun addListener(listener: () -> Unit): Release { if (listeners.isEmpty()) activate() @@ -76,7 +76,7 @@ abstract class BaseListenable : Listenable { * * @see BaseListenable */ -abstract class BaseReactive(start: ReactiveState = ReactiveState.notReady) : Reactive, BaseListenable() { +public abstract class BaseReactive(start: ReactiveState = ReactiveState.notReady) : Reactive, BaseListenable() { override var state: ReactiveState = start protected set(value) { if (field.raw !== value.raw && field != value) { @@ -95,7 +95,7 @@ abstract class BaseReactive(start: ReactiveState = ReactiveState.notReady) * * @see BaseListenable */ -abstract class BaseReactiveValue(start: T) : ReactiveValue, BaseListenable() { +public abstract class BaseReactiveValue(start: T) : ReactiveValue, BaseListenable() { override var value: T = start set(value) { @Suppress("SuspiciousEqualsCombination") diff --git a/src/commonMain/kotlin/com/lightningkite/reactive/core/coreInterfaces.kt b/src/commonMain/kotlin/com/lightningkite/reactive/core/coreInterfaces.kt index 2af73fc..6440e18 100644 --- a/src/commonMain/kotlin/com/lightningkite/reactive/core/coreInterfaces.kt +++ b/src/commonMain/kotlin/com/lightningkite/reactive/core/coreInterfaces.kt @@ -28,7 +28,7 @@ import kotlin.reflect.KProperty * release() // Safe to call again, no effect * ``` */ -typealias Release = () -> Unit +public typealias Release = () -> Unit /** * Represents a resource that can be used and released. @@ -36,11 +36,11 @@ typealias Release = () -> Unit * * @see Listenable */ -interface ResourceUse { +public interface ResourceUse { /** * Begins using the resource. Returns a function to stop using the resource. */ - fun beginUse(): Release + public fun beginUse(): Release } @@ -52,16 +52,16 @@ interface ResourceUse { * * @see ResourceUse */ -interface Listenable : ResourceUse { +public interface Listenable : ResourceUse { /** * Adds the [listener] to be called every time this event fires. * @return a [Release] handle to remove the [listener] that was added. Removing multiple times should not cause issues. */ - fun addListener(listener: () -> Unit): Release + public fun addListener(listener: () -> Unit): Release override fun beginUse(): Release = addListener { } - object Never: Listenable { + public object Never: Listenable { public val NOOP_RELEASE: Release = {} override fun addListener(listener: () -> Unit): Release = NOOP_RELEASE @@ -72,7 +72,7 @@ interface Listenable : ResourceUse { * Adds a listener and immediately runs it once. * @return a [Release] handle to remove the listener. */ -fun Listenable.addAndRunListener(listener: () -> Unit): Release { +public fun Listenable.addAndRunListener(listener: () -> Unit): Release { val release = addListener(listener) listener() return release @@ -91,7 +91,7 @@ fun Listenable.addAndRunListener(listener: () -> Unit): Release { * @see ReactiveState * @see com.lightningkite.reactive.context.ReactiveContext */ -interface Reactive : Listenable { +public interface Reactive : Listenable { /** * The current state. * @@ -100,26 +100,26 @@ interface Reactive : Listenable { * a value they are no longer keeping up to date. Anything else is a promise that the state is * current, which is what lets callers read it without subscribing. */ - val state: ReactiveState + public val state: ReactiveState - object Never: Reactive { + public object Never: Reactive { override val state: ReactiveState get() = ReactiveState.notReady override fun addListener(listener: () -> Unit): Release = Listenable.Never.NOOP_RELEASE } - companion object Companion { + public companion object Companion { /** * Used to report exceptions thrown in listeners or reactive calculations. */ - var reportException: (Throwable) -> Unit = { it.printStackTrace() } + public var reportException: (Throwable) -> Unit = { it.printStackTrace() } } } /** * Represents a mutable value that can be set asynchronously. */ -interface Mutable { - suspend infix fun set(value: T) +public interface Mutable { + public suspend infix fun set(value: T) } /** @@ -132,7 +132,7 @@ interface Mutable { * @see Reactive * @see Mutable */ -interface MutableReactive : Reactive, Mutable { +public interface MutableReactive : Reactive, Mutable { /** * 'Lenses' a new type from this [MutableReactive]. This is useful when translating one @@ -156,7 +156,7 @@ interface MutableReactive : Reactive, Mutable { * ) * ``` */ - fun lens( + public fun lens( get: (T) -> L, set: (L) -> T ): MutableReactive = SetLens(this, get, set) @@ -185,7 +185,7 @@ interface MutableReactive : Reactive, Mutable { * ) * ``` */ - fun lens( + public fun lens( get: (T) -> L, modify: (T, L) -> T ): MutableReactive = ModifyLens(this, get, modify) @@ -202,8 +202,8 @@ interface MutableReactive : Reactive, Mutable { * * @see Reactive */ -interface ReactiveValue : Reactive, ReadOnlyProperty { - val value: T +public interface ReactiveValue : Reactive, ReadOnlyProperty { + public val value: T // A ReactiveValue always has a value, so its state can never be notReady or notActive. That // makes it the wrong interface for anything that only maintains a value while listened to. @@ -219,8 +219,8 @@ interface ReactiveValue : Reactive, ReadOnlyProperty { * * @see Mutable */ -interface MutableValue: Mutable { - infix fun valueSet(value: T) +public interface MutableValue: Mutable { + public infix fun valueSet(value: T) override suspend fun set(value: T) { valueSet(value) } } @@ -229,14 +229,14 @@ interface MutableValue: Mutable { * * Combines [MutableReactive] and [ReactiveValue]. */ -interface MutableWithReactiveValue : MutableReactive, ReactiveValue +public interface MutableWithReactiveValue : MutableReactive, ReactiveValue /** * A [Reactive] that can be synchronously modified. * * Combines [MutableReactive] and [MutableValue]. */ -interface ReactiveWithMutableValue : MutableReactive, MutableValue +public interface ReactiveWithMutableValue : MutableReactive, MutableValue /** * Represents a mutable reactive value that can be modified and observed for changes. @@ -251,7 +251,7 @@ interface ReactiveWithMutableValue : MutableReactive, MutableValue * @see MutableWithReactiveValue * @see ReactiveWithMutableValue */ -interface MutableReactiveValue : MutableValue, ReactiveValue, +public interface MutableReactiveValue : MutableValue, ReactiveValue, // Interfaces below are just for typing convenience, they are already implemented by intersection of MutableSignal and ValueSignal MutableReactive, MutableWithReactiveValue, diff --git a/src/commonMain/kotlin/com/lightningkite/reactive/core/processes.kt b/src/commonMain/kotlin/com/lightningkite/reactive/core/processes.kt index 4891cef..dcde6bb 100644 --- a/src/commonMain/kotlin/com/lightningkite/reactive/core/processes.kt +++ b/src/commonMain/kotlin/com/lightningkite/reactive/core/processes.kt @@ -7,12 +7,12 @@ import kotlinx.coroutines.launch import kotlin.jvm.JvmName -interface Emitter: CoroutineScope { - fun emit(value: T) +public interface Emitter: CoroutineScope { + public fun emit(value: T) } @JvmName("reactiveProcessImplicit") -fun CoroutineScope.reactiveProcess(emitter: suspend Emitter.() -> Unit): Reactive { +public fun CoroutineScope.reactiveProcess(emitter: suspend Emitter.() -> Unit): Reactive { val prop = LateInitSignal() launch { emitter(object : Emitter, CoroutineScope by this { @@ -25,7 +25,7 @@ fun CoroutineScope.reactiveProcess(emitter: suspend Emitter.() -> Unit): } // The two below only run their emitter while something is listening, so with no listeners they // report notActive: whatever the emitter last produced is no longer being kept up to date. -fun reactiveProcess(scope: CoroutineScope = AppScope, emitter: suspend Emitter.() -> Unit): Reactive { +public fun reactiveProcess(scope: CoroutineScope = AppScope, emitter: suspend Emitter.() -> Unit): Reactive { return object: BaseReactive(ReactiveState.notActive) { var job: Job? = null override fun activate() { @@ -45,7 +45,7 @@ fun reactiveProcess(scope: CoroutineScope = AppScope, emitter: suspend Emitt } } } -fun rawReactiveProcess(scope: CoroutineScope = AppScope, emitter: suspend Emitter>.() -> Unit): Reactive { +public fun rawReactiveProcess(scope: CoroutineScope = AppScope, emitter: suspend Emitter>.() -> Unit): Reactive { return object: BaseReactive(ReactiveState.notActive) { var job: Job? = null override fun activate() { diff --git a/src/commonMain/kotlin/com/lightningkite/reactive/core/signals.kt b/src/commonMain/kotlin/com/lightningkite/reactive/core/signals.kt index 8587986..1718e56 100644 --- a/src/commonMain/kotlin/com/lightningkite/reactive/core/signals.kt +++ b/src/commonMain/kotlin/com/lightningkite/reactive/core/signals.kt @@ -8,7 +8,7 @@ import kotlin.jvm.JvmInline * A reactive value that exposes its state and allows direct mutation. * Used for low-level reactive state management. */ -class RawReactive(start: ReactiveState = ReactiveState.notReady) : BaseReactive(start) { +public class RawReactive(start: ReactiveState = ReactiveState.notReady) : BaseReactive(start) { override var state: ReactiveState get() = super.state public set(value) { super.state = value } @@ -18,13 +18,13 @@ class RawReactive(start: ReactiveState = ReactiveState.notReady) : BaseRea * A basic implementation of a listenable object. * Can invoke all listeners and provides a unique identifier for debugging. */ -class BasicListenable : BaseListenable() { +public class BasicListenable : BaseListenable() { private var id = Random.nextInt(0, 100000) override fun toString(): String { return "BasicListenable($id)" } - fun invokeAll() { + public fun invokeAll() { super.invokeAllListeners() } } @@ -47,18 +47,18 @@ class BasicListenable : BaseListenable() { * number.value = 2 // prints "Number: 2" * ``` */ -class Signal(startValue: T) : MutableReactiveValue, BaseReactiveValue(startValue) +public class Signal(startValue: T) : MutableReactiveValue, BaseReactiveValue(startValue) /** * A reactive value that can be set after initialization and unset to a not-ready state. * Useful for cases where the value is not available at construction time. */ -class LateInitSignal() : ReactiveWithMutableValue, BaseReactive() { +public class LateInitSignal() : ReactiveWithMutableValue, BaseReactive() { override fun valueSet(value: T) { state = ReactiveState(value) } - fun unset() { + public fun unset() { state = ReactiveState.notReady } } @@ -71,6 +71,6 @@ class LateInitSignal() : ReactiveWithMutableValue, BaseReactive() { * no overhead. */ @JvmInline -value class Constant(override val value: T) : ReactiveValue { +public value class Constant(override val value: T) : ReactiveValue { override fun addListener(listener: () -> Unit): Release = Listenable.Never.NOOP_RELEASE } diff --git a/src/commonMain/kotlin/com/lightningkite/reactive/deprecated.kt b/src/commonMain/kotlin/com/lightningkite/reactive/deprecated.kt index 9edca9a..a875866 100644 --- a/src/commonMain/kotlin/com/lightningkite/reactive/deprecated.kt +++ b/src/commonMain/kotlin/com/lightningkite/reactive/deprecated.kt @@ -32,84 +32,84 @@ import kotlin.jvm.JvmName @Deprecated("Only exists to not break imports", level = DeprecationLevel.ERROR) -fun Nothing.bind(): Nothing = TODO() +public fun Nothing.bind(): Nothing = TODO() // Naming deprecations @Deprecated("Use Reactive", ReplaceWith("Reactive", "com.lightningkite.reactive.core")) -typealias Readable = Reactive +public typealias Readable = Reactive @Deprecated("Use MutableReactive", ReplaceWith("MutableReactive", "com.lightningkite.reactive.core")) -typealias Writable = MutableReactive +public typealias Writable = MutableReactive @Deprecated("Use Remember", ReplaceWith("Remember", "com.lightningkite.reactive.core")) -typealias SharedReadable = Remember +public typealias SharedReadable = Remember @Deprecated("Use ReactiveValue", ReplaceWith("ReactiveValue", "com.lightningkite.reactive.core")) -typealias ImmediateReadable = ReactiveValue +public typealias ImmediateReadable = ReactiveValue @Deprecated("Use MutableReactiveValue", ReplaceWith("MutableReactiveValue", "com.lightningkite.reactive.core")) -typealias ImmediateWritable = MutableReactiveValue +public typealias ImmediateWritable = MutableReactiveValue @Deprecated("Use ReactiveWithMutableValue", ReplaceWith("ReactiveWithMutableValue", "com.lightningkite.reactive.core")) -typealias ReadableWithImmediateWrite = ReactiveWithMutableValue +public typealias ReadableWithImmediateWrite = ReactiveWithMutableValue @Deprecated("Use MutableWithReactiveValue", ReplaceWith("MutableWithReactiveValue", "com.lightningkite.reactive.core")) -typealias ImmediateReadableWithWrite = MutableWithReactiveValue +public typealias ImmediateReadableWithWrite = MutableWithReactiveValue @Deprecated("Use Signal", ReplaceWith("Signal", "com.lightningkite.reactive.core")) -typealias Property = Signal +public typealias Property = Signal @Deprecated("Use MutableRemember", ReplaceWith("MutableRemember", "com.lightningkite.reactive.core")) -typealias LazyProperty = MutableRemember +public typealias LazyProperty = MutableRemember @Deprecated("Use DebounceReactive", ReplaceWith("DebounceReactive", "com.lightningkite.reactive.extensions")) -typealias DebounceReadable = DebounceReactive +public typealias DebounceReadable = DebounceReactive @OptIn(InternalReactiveApi::class) @Deprecated("Use InternalSignalWrapper", ReplaceWith("InternalSignalWrapper", "com.lightningkite.reactive.core")) -typealias InternalReadableWrapper = InternalReactiveWrapper +public typealias InternalReadableWrapper = InternalReactiveWrapper @Deprecated("Use RawReactive", ReplaceWith("RawReactive", "com.lightningkite.reactive.core")) -typealias RawReadable = RawReactive +public typealias RawReadable = RawReactive @Deprecated("Use ReactiveState", ReplaceWith("ReactiveState", "com.lightningkite.reactive.core")) -typealias ReadableState = ReactiveState +public typealias ReadableState = ReactiveState @Deprecated("Use SignalEmitter", ReplaceWith("SignalEmitter", "com.lightningkite.reactive.core")) -typealias ReadableEmitter = Emitter +public typealias ReadableEmitter = Emitter @Deprecated("Use MutableValue", ReplaceWith("MutableValue", "com.lightningkite.reactive.core")) -typealias ImmediateWriteOnly = MutableValue +public typealias ImmediateWriteOnly = MutableValue @Deprecated("Use BaseReactiveValue", ReplaceWith("BaseReactiveValue", "com.lightningkite.reactive.core")) -typealias BaseImmediateReadable = BaseReactiveValue +public typealias BaseImmediateReadable = BaseReactiveValue @Deprecated("Use BaseReactive", ReplaceWith("BaseReactive", "com.lightningkite.reactive.core")) -typealias BaseReadable = BaseReactive +public typealias BaseReadable = BaseReactive @Deprecated("Use BaseReactive", ReplaceWith("BaseReactive", "com.lightningkite.reactive.core")) -typealias BaseWritable = BaseReactive +public typealias BaseWritable = BaseReactive @Deprecated("Use BaseReactiveValue", ReplaceWith("BaseReactiveValue", "com.lightningkite.reactive.core")) -typealias BaseReadWrite = BaseReactiveValue +public typealias BaseReadWrite = BaseReactiveValue @Deprecated("Use LateInitReactiveValue", ReplaceWith("LateInitReactiveValue", "com.lightningkite.reactive.core")) -typealias LateInitProperty = LateInitSignal +public typealias LateInitProperty = LateInitSignal @Deprecated("Use remember", ReplaceWith("remember", "com.lightningkite.reactive.core")) -fun shared(coroutineContext: CoroutineContext = Dispatchers.Unconfined, useLastWhileLoading: Boolean = false, action: ReactiveContext.() -> T): Reactive = +public fun shared(coroutineContext: CoroutineContext = Dispatchers.Unconfined, useLastWhileLoading: Boolean = false, action: ReactiveContext.() -> T): Reactive = remember(coroutineContext, useLastWhileLoading, null, action = action) @Deprecated("Use reactiveProcess", ReplaceWith("reactiveProcess", "com.lightningkite.reactive.core")) -fun sharedProcess(scope: CoroutineScope = AppScope, emitter: suspend Emitter.() -> Unit): Reactive = reactiveProcess(scope, emitter) +public fun sharedProcess(scope: CoroutineScope = AppScope, emitter: suspend Emitter.() -> Unit): Reactive = reactiveProcess(scope, emitter) @Deprecated("Use reactiveProcess", ReplaceWith("reactiveProcess", "com.lightningkite.reactive.core")) @JvmName("sharedProcessReceiving") -fun CoroutineScope.sharedProcess(emitter: suspend Emitter.() -> Unit): Reactive = reactiveProcess(emitter) +public fun CoroutineScope.sharedProcess(emitter: suspend Emitter.() -> Unit): Reactive = reactiveProcess(emitter) @Deprecated("Use reactiveState", ReplaceWith("reactiveState", "com.lightningkite.reactive.core")) -inline fun readableState(action: () -> T): ReactiveState = reactiveState(action) +public inline fun readableState(action: () -> T): ReactiveState = reactiveState(action) @Deprecated("Use toReactiveState", ReplaceWith("toReactiveState", "com.lightningkite.reactive.core")) -fun Result.toReadableState(): ReactiveState = toReactiveState() \ No newline at end of file +public fun Result.toReadableState(): ReactiveState = toReactiveState() \ No newline at end of file diff --git a/src/commonMain/kotlin/com/lightningkite/reactive/extensions/Listenable.ext.kt b/src/commonMain/kotlin/com/lightningkite/reactive/extensions/Listenable.ext.kt index b6efdc2..c28e095 100644 --- a/src/commonMain/kotlin/com/lightningkite/reactive/extensions/Listenable.ext.kt +++ b/src/commonMain/kotlin/com/lightningkite/reactive/extensions/Listenable.ext.kt @@ -6,22 +6,22 @@ import com.lightningkite.reactive.core.Reactive import com.lightningkite.reactive.core.ReactiveState import com.lightningkite.reactive.core.ReactiveValue -inline fun Reactive.addStateListener(crossinline listener: (ReactiveState) -> Unit): Release { +public inline fun Reactive.addStateListener(crossinline listener: (ReactiveState) -> Unit): Release { return addListener { listener(state) } } -inline fun Reactive.addAndRunStateListener(crossinline listener: (ReactiveState) -> Unit): Release { +public inline fun Reactive.addAndRunStateListener(crossinline listener: (ReactiveState) -> Unit): Release { val listener: () -> Unit = { listener(state) } val release = addListener(listener) listener() return release } -inline fun ReactiveValue.addValueListener(crossinline listener: (T) -> Unit): Release { +public inline fun ReactiveValue.addValueListener(crossinline listener: (T) -> Unit): Release { return addListener { listener(value) } } -inline fun ReactiveValue.addAndRunValueListener(crossinline listener: (T) -> Unit): Release { +public inline fun ReactiveValue.addAndRunValueListener(crossinline listener: (T) -> Unit): Release { val listener: () -> Unit = { listener(value) } val release = addListener(listener) listener() diff --git a/src/commonMain/kotlin/com/lightningkite/reactive/extensions/ReactiveState.ext.kt b/src/commonMain/kotlin/com/lightningkite/reactive/extensions/ReactiveState.ext.kt index 24f9465..47332df 100644 --- a/src/commonMain/kotlin/com/lightningkite/reactive/extensions/ReactiveState.ext.kt +++ b/src/commonMain/kotlin/com/lightningkite/reactive/extensions/ReactiveState.ext.kt @@ -2,7 +2,7 @@ package com.lightningkite.reactive.extensions import com.lightningkite.reactive.core.ReactiveState -inline fun ReactiveState.getOrElse(default: () -> R): R { +public inline fun ReactiveState.getOrElse(default: () -> R): R { return handle( success = { it }, exception = { default() }, diff --git a/src/commonMain/kotlin/com/lightningkite/reactive/extensions/WaitForNotNull.kt b/src/commonMain/kotlin/com/lightningkite/reactive/extensions/WaitForNotNull.kt index bbb098f..1d3b1d2 100644 --- a/src/commonMain/kotlin/com/lightningkite/reactive/extensions/WaitForNotNull.kt +++ b/src/commonMain/kotlin/com/lightningkite/reactive/extensions/WaitForNotNull.kt @@ -22,14 +22,14 @@ internal class WaitForNotNull(val wraps: Reactive) : Reactive { override fun equals(other: Any?): Boolean = other is WaitForNotNull<*> && this.wraps == other.wraps } -val Reactive.waitForNotNull: Reactive get() = WaitForNotNull(this) +public val Reactive.waitForNotNull: Reactive get() = WaitForNotNull(this) -val MutableReactive.waitForNotNull: MutableReactive get() = +public val MutableReactive.waitForNotNull: MutableReactive get() = object : MutableReactive, Reactive by (this as Reactive).waitForNotNull { // DO NOT REMOVE THE TYPECAST override suspend fun set(value: T) = this@waitForNotNull.set(value) } -suspend fun Reactive.awaitNotNull(): T { +public suspend fun Reactive.awaitNotNull(): T { val basis = await() return basis ?: suspendCancellableCoroutine { } } \ No newline at end of file diff --git a/src/commonMain/kotlin/com/lightningkite/reactive/extensions/commaString.kt b/src/commonMain/kotlin/com/lightningkite/reactive/extensions/commaString.kt index e78f3a7..5a12a2c 100644 --- a/src/commonMain/kotlin/com/lightningkite/reactive/extensions/commaString.kt +++ b/src/commonMain/kotlin/com/lightningkite/reactive/extensions/commaString.kt @@ -4,7 +4,7 @@ import kotlin.math.pow import kotlin.math.roundToInt -fun Double.toStringNoExponential(): String { +public fun Double.toStringNoExponential(): String { val preDecimal = toLong().toString() val r = rem(1) if (r == 0.0) return preDecimal @@ -14,15 +14,15 @@ fun Double.toStringNoExponential(): String { else return preDecimal + "." + postDecimal.toString().padStart(availableDigits, '0').trimEnd('0') } -fun Double.commaString(): String { +public fun Double.commaString(): String { val clean = this.toStringNoExponential().filter { it.isDigit() || it in setOf('.', '-') } val preDecimal = clean.substringBefore('.').reversed().chunked(3) { it.reversed() }.reversed().joinToString(",") val postDecimal = clean.substringAfter('.', "") return if (clean.contains('.')) "$preDecimal.$postDecimal" else preDecimal } -fun Int.commaString(): String { +public fun Int.commaString(): String { return toString().substringBefore('.').reversed().chunked(3) { it.reversed() }.reversed().joinToString(",") } -fun Long.commaString(): String { +public fun Long.commaString(): String { return toString().substringBefore('.').reversed().chunked(3) { it.reversed() }.reversed().joinToString(",") } diff --git a/src/commonMain/kotlin/com/lightningkite/reactive/extensions/commonLenses.kt b/src/commonMain/kotlin/com/lightningkite/reactive/extensions/commonLenses.kt index 0db43fa..6eca470 100644 --- a/src/commonMain/kotlin/com/lightningkite/reactive/extensions/commonLenses.kt +++ b/src/commonMain/kotlin/com/lightningkite/reactive/extensions/commonLenses.kt @@ -9,78 +9,78 @@ import com.lightningkite.reactive.lensing.validation.MutableValidatedValue import kotlin.collections.plus import kotlin.jvm.JvmName -infix fun MutableReactive.equalTo(value: T): MutableReactive = lens( +public infix fun MutableReactive.equalTo(value: T): MutableReactive = lens( get = { it == value }, modify = { o, it -> if (it) value else o } ) -fun MutableReactive.notNull(default: T): MutableReactive = lens( +public fun MutableReactive.notNull(default: T): MutableReactive = lens( get = { it ?: default }, set = { it } ) -fun MutableReactive.nullToBlank(): MutableReactive = lens( +public fun MutableReactive.nullToBlank(): MutableReactive = lens( get = { it ?: "" }, set = { it.takeUnless { it.isBlank() } } ) -infix fun MutableReactive>.contains(value: T): MutableReactive = lens( +public infix fun MutableReactive>.contains(value: T): MutableReactive = lens( get = { value in it }, modify = { items, bool -> if (bool) items + value else items - value } ) @JvmName("containsList") -infix fun MutableReactive>.contains(value: T): MutableReactive = lens( +public infix fun MutableReactive>.contains(value: T): MutableReactive = lens( get = { value in it }, modify = { items, bool -> if (bool) items + value else items - value } ) @JvmName("writableStringAsDouble") -fun MutableReactive.asDouble(): MutableReactive = lens(get = { it.filter { it.isDigit() || it == '.' }.toDoubleOrNull() }, set = { it?.commaString() ?: "" }) +public fun MutableReactive.asDouble(): MutableReactive = lens(get = { it.filter { it.isDigit() || it == '.' }.toDoubleOrNull() }, set = { it?.commaString() ?: "" }) @JvmName("writableStringAsFloat") -fun MutableReactive.asFloat(): MutableReactive = lens(get = { it.filter { it.isDigit() || it == '.' }.toFloatOrNull() }, set = { it?.toDouble()?.commaString() ?: "" }) +public fun MutableReactive.asFloat(): MutableReactive = lens(get = { it.filter { it.isDigit() || it == '.' }.toFloatOrNull() }, set = { it?.toDouble()?.commaString() ?: "" }) @JvmName("writableStringAsByte") -fun MutableReactive.asByte(): MutableReactive = lens(get = { it.filter { it.isDigit() || it == '.' }.toByteOrNull() }, set = { it?.toInt()?.commaString() ?: "" }) +public fun MutableReactive.asByte(): MutableReactive = lens(get = { it.filter { it.isDigit() || it == '.' }.toByteOrNull() }, set = { it?.toInt()?.commaString() ?: "" }) @JvmName("writableStringAsShort") -fun MutableReactive.asShort(): MutableReactive = lens(get = { it.filter { it.isDigit() || it == '.' }.toShortOrNull() }, set = { it?.toInt()?.commaString() ?: "" }) +public fun MutableReactive.asShort(): MutableReactive = lens(get = { it.filter { it.isDigit() || it == '.' }.toShortOrNull() }, set = { it?.toInt()?.commaString() ?: "" }) @JvmName("writableStringAsInt") -fun MutableReactive.asInt(): MutableReactive = lens(get = { it.filter { it.isDigit() || it == '.' }.toIntOrNull() }, set = { it?.commaString() ?: "" }) +public fun MutableReactive.asInt(): MutableReactive = lens(get = { it.filter { it.isDigit() || it == '.' }.toIntOrNull() }, set = { it?.commaString() ?: "" }) @JvmName("writableStringAsLong") -fun MutableReactive.asLong(): MutableReactive = lens(get = { it.filter { it.isDigit() || it == '.' }.toLongOrNull() }, set = { it?.commaString() ?: "" }) +public fun MutableReactive.asLong(): MutableReactive = lens(get = { it.filter { it.isDigit() || it == '.' }.toLongOrNull() }, set = { it?.commaString() ?: "" }) @JvmName("writableStringAsByteHex") -fun MutableReactive.asByteHex(): MutableReactive = lens(get = { it.toByteOrNull(16) }, set = { it?.toString(16) ?: "" }) +public fun MutableReactive.asByteHex(): MutableReactive = lens(get = { it.toByteOrNull(16) }, set = { it?.toString(16) ?: "" }) @JvmName("writableStringAsUByteHex") -fun MutableReactive.asUByteHex(): MutableReactive = lens(get = { it.toUByteOrNull(16) }, set = { it?.toString(16) ?: "" }) +public fun MutableReactive.asUByteHex(): MutableReactive = lens(get = { it.toUByteOrNull(16) }, set = { it?.toString(16) ?: "" }) @JvmName("writableStringAsShortHex") -fun MutableReactive.asShortHex(): MutableReactive = lens(get = { it.toShortOrNull(16) }, set = { it?.toString(16) ?: "" }) +public fun MutableReactive.asShortHex(): MutableReactive = lens(get = { it.toShortOrNull(16) }, set = { it?.toString(16) ?: "" }) @JvmName("writableStringAsUShortHex") -fun MutableReactive.asUShortHex(): MutableReactive = lens(get = { it.toUShortOrNull(16) }, set = { it?.toString(16) ?: "" }) +public fun MutableReactive.asUShortHex(): MutableReactive = lens(get = { it.toUShortOrNull(16) }, set = { it?.toString(16) ?: "" }) @JvmName("writableStringAsIntHex") -fun MutableReactive.asIntHex(): MutableReactive = lens(get = { it.toIntOrNull(16) }, set = { it?.toString(16) ?: "" }) +public fun MutableReactive.asIntHex(): MutableReactive = lens(get = { it.toIntOrNull(16) }, set = { it?.toString(16) ?: "" }) @JvmName("writableStringAsUIntHex") -fun MutableReactive.asUIntHex(): MutableReactive = lens(get = { it.toUIntOrNull(16) }, set = { it?.toString(16) ?: "" }) +public fun MutableReactive.asUIntHex(): MutableReactive = lens(get = { it.toUIntOrNull(16) }, set = { it?.toString(16) ?: "" }) @JvmName("writableStringAsLongHex") -fun MutableReactive.asLongHex(): MutableReactive = lens(get = { it.toLongOrNull(16) }, set = { it?.toString(16) ?: "" }) +public fun MutableReactive.asLongHex(): MutableReactive = lens(get = { it.toLongOrNull(16) }, set = { it?.toString(16) ?: "" }) @JvmName("writableStringAsULongHex") -fun MutableReactive.asULongHex(): MutableReactive = lens(get = { it.toULongOrNull(16) }, set = { it?.toString(16) ?: "" }) +public fun MutableReactive.asULongHex(): MutableReactive = lens(get = { it.toULongOrNull(16) }, set = { it?.toString(16) ?: "" }) @JvmName("writableIntAsDoubleNullable") -fun MutableReactive.asDouble(): MutableReactive = lens(get = { it?.toDouble() }, set = { it?.toInt() }) +public fun MutableReactive.asDouble(): MutableReactive = lens(get = { it?.toDouble() }, set = { it?.toInt() }) -fun MutableReactive.nullToZero(): MutableReactive = +public fun MutableReactive.nullToZero(): MutableReactive = object : MutableReactive, Reactive by this { override suspend fun set(value: Double?) { this@nullToZero.set(value ?: 0.0) @@ -88,144 +88,144 @@ fun MutableReactive.nullToZero(): MutableReactive = } @JvmName("writableStringAsDouble") -fun MutableReactiveValue.asDouble(): MutableReactiveValue = lens(get = { it.filter { it.isDigit() || it == '-' || it == '.'}.toDoubleOrNull() }, set = { it?.commaString() ?: "" }) +public fun MutableReactiveValue.asDouble(): MutableReactiveValue = lens(get = { it.filter { it.isDigit() || it == '-' || it == '.'}.toDoubleOrNull() }, set = { it?.commaString() ?: "" }) @JvmName("writableStringAsFloat") -fun MutableReactiveValue.asFloat(): MutableReactiveValue = lens(get = { it.filter { it.isDigit() || it == '-' || it == '.'}.toFloatOrNull() }, set = { it?.toDouble()?.commaString() ?: "" }) +public fun MutableReactiveValue.asFloat(): MutableReactiveValue = lens(get = { it.filter { it.isDigit() || it == '-' || it == '.'}.toFloatOrNull() }, set = { it?.toDouble()?.commaString() ?: "" }) @JvmName("writableStringAsByte") -fun MutableReactiveValue.asByte(): MutableReactiveValue = lens(get = { it.filter { it.isDigit() || it == '-' || it == '.'}.toByteOrNull() }, set = { it?.toInt()?.commaString() ?: "" }) +public fun MutableReactiveValue.asByte(): MutableReactiveValue = lens(get = { it.filter { it.isDigit() || it == '-' || it == '.'}.toByteOrNull() }, set = { it?.toInt()?.commaString() ?: "" }) @JvmName("writableStringAsShort") -fun MutableReactiveValue.asShort(): MutableReactiveValue = lens(get = { it.filter { it.isDigit() || it == '-' || it == '.'}.toShortOrNull() }, set = { it?.toInt()?.commaString() ?: "" }) +public fun MutableReactiveValue.asShort(): MutableReactiveValue = lens(get = { it.filter { it.isDigit() || it == '-' || it == '.'}.toShortOrNull() }, set = { it?.toInt()?.commaString() ?: "" }) @JvmName("writableStringAsInt") -fun MutableReactiveValue.asInt(): MutableReactiveValue = lens(get = { it.filter { it.isDigit() || it == '-' || it == '.'}.toIntOrNull() }, set = { it?.commaString() ?: "" }) +public fun MutableReactiveValue.asInt(): MutableReactiveValue = lens(get = { it.filter { it.isDigit() || it == '-' || it == '.'}.toIntOrNull() }, set = { it?.commaString() ?: "" }) @JvmName("writableStringAsLong") -fun MutableReactiveValue.asLong(): MutableReactiveValue = lens(get = { it.filter { it.isDigit() || it == '-' || it == '.'}.toLongOrNull() }, set = { it?.commaString() ?: "" }) +public fun MutableReactiveValue.asLong(): MutableReactiveValue = lens(get = { it.filter { it.isDigit() || it == '-' || it == '.'}.toLongOrNull() }, set = { it?.commaString() ?: "" }) @JvmName("writableStringAsByteHex") -fun MutableReactiveValue.asByteHex(): MutableReactiveValue = lens(get = { it.toByteOrNull(16) }, set = { it?.toString(16) ?: "" }) +public fun MutableReactiveValue.asByteHex(): MutableReactiveValue = lens(get = { it.toByteOrNull(16) }, set = { it?.toString(16) ?: "" }) @JvmName("writableStringAsUByteHex") -fun MutableReactiveValue.asUByteHex(): MutableReactiveValue = lens(get = { it.toUByteOrNull(16) }, set = { it?.toString(16) ?: "" }) +public fun MutableReactiveValue.asUByteHex(): MutableReactiveValue = lens(get = { it.toUByteOrNull(16) }, set = { it?.toString(16) ?: "" }) @JvmName("writableStringAsShortHex") -fun MutableReactiveValue.asShortHex(): MutableReactiveValue = lens(get = { it.toShortOrNull(16) }, set = { it?.toString(16) ?: "" }) +public fun MutableReactiveValue.asShortHex(): MutableReactiveValue = lens(get = { it.toShortOrNull(16) }, set = { it?.toString(16) ?: "" }) @JvmName("writableStringAsUShortHex") -fun MutableReactiveValue.asUShortHex(): MutableReactiveValue = lens(get = { it.toUShortOrNull(16) }, set = { it?.toString(16) ?: "" }) +public fun MutableReactiveValue.asUShortHex(): MutableReactiveValue = lens(get = { it.toUShortOrNull(16) }, set = { it?.toString(16) ?: "" }) @JvmName("writableStringAsIntHex") -fun MutableReactiveValue.asIntHex(): MutableReactiveValue = lens(get = { it.toIntOrNull(16) }, set = { it?.toString(16) ?: "" }) +public fun MutableReactiveValue.asIntHex(): MutableReactiveValue = lens(get = { it.toIntOrNull(16) }, set = { it?.toString(16) ?: "" }) @JvmName("writableStringAsUIntHex") -fun MutableReactiveValue.asUIntHex(): MutableReactiveValue = lens(get = { it.toUIntOrNull(16) }, set = { it?.toString(16) ?: "" }) +public fun MutableReactiveValue.asUIntHex(): MutableReactiveValue = lens(get = { it.toUIntOrNull(16) }, set = { it?.toString(16) ?: "" }) @JvmName("writableStringAsLongHex") -fun MutableReactiveValue.asLongHex(): MutableReactiveValue = lens(get = { it.toLongOrNull(16) }, set = { it?.toString(16) ?: "" }) +public fun MutableReactiveValue.asLongHex(): MutableReactiveValue = lens(get = { it.toLongOrNull(16) }, set = { it?.toString(16) ?: "" }) @JvmName("writableStringAsULongHex") -fun MutableReactiveValue.asULongHex(): MutableReactiveValue = lens(get = { it.toULongOrNull(16) }, set = { it?.toString(16) ?: "" }) +public fun MutableReactiveValue.asULongHex(): MutableReactiveValue = lens(get = { it.toULongOrNull(16) }, set = { it?.toString(16) ?: "" }) @JvmName("writableIntAsDoubleNullable") -fun MutableReactiveValue.asDouble(): MutableReactiveValue = lens(get = { it?.toDouble() }, set = { it?.toInt() }) +public fun MutableReactiveValue.asDouble(): MutableReactiveValue = lens(get = { it?.toDouble() }, set = { it?.toInt() }) // Validated variants -fun MutableValidated.nullToBlank(): MutableValidated = lens( +public fun MutableValidated.nullToBlank(): MutableValidated = lens( get = { it ?: "" }, set = { it.takeUnless { it.isBlank() } } ) @JvmName("writableStringAsDoubleValidated") -fun MutableValidated.asDouble(): MutableValidated = lens(get = { it.filter { it.isDigit() || it == '.' }.toDoubleOrNull() }, set = { it?.commaString() ?: "" }) +public fun MutableValidated.asDouble(): MutableValidated = lens(get = { it.filter { it.isDigit() || it == '.' }.toDoubleOrNull() }, set = { it?.commaString() ?: "" }) @JvmName("writableStringAsFloatValidated") -fun MutableValidated.asFloat(): MutableValidated = lens(get = { it.filter { it.isDigit() || it == '.' }.toFloatOrNull() }, set = { it?.toDouble()?.commaString() ?: "" }) +public fun MutableValidated.asFloat(): MutableValidated = lens(get = { it.filter { it.isDigit() || it == '.' }.toFloatOrNull() }, set = { it?.toDouble()?.commaString() ?: "" }) @JvmName("writableStringAsByteValidated") -fun MutableValidated.asByte(): MutableValidated = lens(get = { it.filter { it.isDigit() || it == '.' }.toByteOrNull() }, set = { it?.toInt()?.commaString() ?: "" }) +public fun MutableValidated.asByte(): MutableValidated = lens(get = { it.filter { it.isDigit() || it == '.' }.toByteOrNull() }, set = { it?.toInt()?.commaString() ?: "" }) @JvmName("writableStringAsShortValidated") -fun MutableValidated.asShort(): MutableValidated = lens(get = { it.filter { it.isDigit() || it == '.' }.toShortOrNull() }, set = { it?.toInt()?.commaString() ?: "" }) +public fun MutableValidated.asShort(): MutableValidated = lens(get = { it.filter { it.isDigit() || it == '.' }.toShortOrNull() }, set = { it?.toInt()?.commaString() ?: "" }) @JvmName("writableStringAsIntValidated") -fun MutableValidated.asInt(): MutableValidated = lens(get = { it.filter { it.isDigit() || it == '.' }.toIntOrNull() }, set = { it?.commaString() ?: "" }) +public fun MutableValidated.asInt(): MutableValidated = lens(get = { it.filter { it.isDigit() || it == '.' }.toIntOrNull() }, set = { it?.commaString() ?: "" }) @JvmName("writableStringAsLongValidated") -fun MutableValidated.asLong(): MutableValidated = lens(get = { it.filter { it.isDigit() || it == '.' }.toLongOrNull() }, set = { it?.commaString() ?: "" }) +public fun MutableValidated.asLong(): MutableValidated = lens(get = { it.filter { it.isDigit() || it == '.' }.toLongOrNull() }, set = { it?.commaString() ?: "" }) @JvmName("writableStringAsByteHexValidated") -fun MutableValidated.asByteHex(): MutableValidated = lens(get = { it.toByteOrNull(16) }, set = { it?.toString(16) ?: "" }) +public fun MutableValidated.asByteHex(): MutableValidated = lens(get = { it.toByteOrNull(16) }, set = { it?.toString(16) ?: "" }) @JvmName("writableStringAsUByteHexValidated") -fun MutableValidated.asUByteHex(): MutableValidated = lens(get = { it.toUByteOrNull(16) }, set = { it?.toString(16) ?: "" }) +public fun MutableValidated.asUByteHex(): MutableValidated = lens(get = { it.toUByteOrNull(16) }, set = { it?.toString(16) ?: "" }) @JvmName("writableStringAsShortHexValidated") -fun MutableValidated.asShortHex(): MutableValidated = lens(get = { it.toShortOrNull(16) }, set = { it?.toString(16) ?: "" }) +public fun MutableValidated.asShortHex(): MutableValidated = lens(get = { it.toShortOrNull(16) }, set = { it?.toString(16) ?: "" }) @JvmName("writableStringAsUShortHexValidated") -fun MutableValidated.asUShortHex(): MutableValidated = lens(get = { it.toUShortOrNull(16) }, set = { it?.toString(16) ?: "" }) +public fun MutableValidated.asUShortHex(): MutableValidated = lens(get = { it.toUShortOrNull(16) }, set = { it?.toString(16) ?: "" }) @JvmName("writableStringAsIntHexValidated") -fun MutableValidated.asIntHex(): MutableValidated = lens(get = { it.toIntOrNull(16) }, set = { it?.toString(16) ?: "" }) +public fun MutableValidated.asIntHex(): MutableValidated = lens(get = { it.toIntOrNull(16) }, set = { it?.toString(16) ?: "" }) @JvmName("writableStringAsUIntHexValidated") -fun MutableValidated.asUIntHex(): MutableValidated = lens(get = { it.toUIntOrNull(16) }, set = { it?.toString(16) ?: "" }) +public fun MutableValidated.asUIntHex(): MutableValidated = lens(get = { it.toUIntOrNull(16) }, set = { it?.toString(16) ?: "" }) @JvmName("writableStringAsLongHexValidated") -fun MutableValidated.asLongHex(): MutableValidated = lens(get = { it.toLongOrNull(16) }, set = { it?.toString(16) ?: "" }) +public fun MutableValidated.asLongHex(): MutableValidated = lens(get = { it.toLongOrNull(16) }, set = { it?.toString(16) ?: "" }) @JvmName("writableStringAsULongHexValidated") -fun MutableValidated.asULongHex(): MutableValidated = lens(get = { it.toULongOrNull(16) }, set = { it?.toString(16) ?: "" }) +public fun MutableValidated.asULongHex(): MutableValidated = lens(get = { it.toULongOrNull(16) }, set = { it?.toString(16) ?: "" }) @JvmName("writableIntAsDoubleNullableValidated") -fun MutableValidated.asDouble(): MutableValidated = lens(get = { it?.toDouble() }, set = { it?.toInt() }) +public fun MutableValidated.asDouble(): MutableValidated = lens(get = { it?.toDouble() }, set = { it?.toInt() }) @JvmName("writableStringAsDoubleValidatedValue") -fun MutableValidatedValue.asDouble(): MutableValidatedValue = lens(get = { it.filter { it.isDigit() || it == '-' || it == '.'}.toDoubleOrNull() }, set = { it?.commaString() ?: "" }) +public fun MutableValidatedValue.asDouble(): MutableValidatedValue = lens(get = { it.filter { it.isDigit() || it == '-' || it == '.'}.toDoubleOrNull() }, set = { it?.commaString() ?: "" }) @JvmName("writableStringAsFloatValidatedValue") -fun MutableValidatedValue.asFloat(): MutableValidatedValue = lens(get = { it.filter { it.isDigit() || it == '-' || it == '.'}.toFloatOrNull() }, set = { it?.toDouble()?.commaString() ?: "" }) +public fun MutableValidatedValue.asFloat(): MutableValidatedValue = lens(get = { it.filter { it.isDigit() || it == '-' || it == '.'}.toFloatOrNull() }, set = { it?.toDouble()?.commaString() ?: "" }) @JvmName("writableStringAsByteValidatedValue") -fun MutableValidatedValue.asByte(): MutableValidatedValue = lens(get = { it.filter { it.isDigit() || it == '-' || it == '.'}.toByteOrNull() }, set = { it?.toInt()?.commaString() ?: "" }) +public fun MutableValidatedValue.asByte(): MutableValidatedValue = lens(get = { it.filter { it.isDigit() || it == '-' || it == '.'}.toByteOrNull() }, set = { it?.toInt()?.commaString() ?: "" }) @JvmName("writableStringAsShortValidatedValue") -fun MutableValidatedValue.asShort(): MutableValidatedValue = lens(get = { it.filter { it.isDigit() || it == '-' || it == '.'}.toShortOrNull() }, set = { it?.toInt()?.commaString() ?: "" }) +public fun MutableValidatedValue.asShort(): MutableValidatedValue = lens(get = { it.filter { it.isDigit() || it == '-' || it == '.'}.toShortOrNull() }, set = { it?.toInt()?.commaString() ?: "" }) @JvmName("writableStringAsIntValidatedValue") -fun MutableValidatedValue.asInt(): MutableValidatedValue = lens(get = { it.filter { it.isDigit() || it == '-' || it == '.'}.toIntOrNull() }, set = { it?.commaString() ?: "" }) +public fun MutableValidatedValue.asInt(): MutableValidatedValue = lens(get = { it.filter { it.isDigit() || it == '-' || it == '.'}.toIntOrNull() }, set = { it?.commaString() ?: "" }) @JvmName("writableStringAsLongValidatedValue") -fun MutableValidatedValue.asLong(): MutableValidatedValue = lens(get = { it.filter { it.isDigit() || it == '-' || it == '.'}.toLongOrNull() }, set = { it?.commaString() ?: "" }) +public fun MutableValidatedValue.asLong(): MutableValidatedValue = lens(get = { it.filter { it.isDigit() || it == '-' || it == '.'}.toLongOrNull() }, set = { it?.commaString() ?: "" }) @JvmName("writableStringAsByteHexValidatedValue") -fun MutableValidatedValue.asByteHex(): MutableValidatedValue = lens(get = { it.toByteOrNull(16) }, set = { it?.toString(16) ?: "" }) +public fun MutableValidatedValue.asByteHex(): MutableValidatedValue = lens(get = { it.toByteOrNull(16) }, set = { it?.toString(16) ?: "" }) @JvmName("writableStringAsUByteHexValidatedValue") -fun MutableValidatedValue.asUByteHex(): MutableValidatedValue = lens(get = { it.toUByteOrNull(16) }, set = { it?.toString(16) ?: "" }) +public fun MutableValidatedValue.asUByteHex(): MutableValidatedValue = lens(get = { it.toUByteOrNull(16) }, set = { it?.toString(16) ?: "" }) @JvmName("writableStringAsShortHexValidatedValue") -fun MutableValidatedValue.asShortHex(): MutableValidatedValue = lens(get = { it.toShortOrNull(16) }, set = { it?.toString(16) ?: "" }) +public fun MutableValidatedValue.asShortHex(): MutableValidatedValue = lens(get = { it.toShortOrNull(16) }, set = { it?.toString(16) ?: "" }) @JvmName("writableStringAsUShortHexValidatedValue") -fun MutableValidatedValue.asUShortHex(): MutableValidatedValue = lens(get = { it.toUShortOrNull(16) }, set = { it?.toString(16) ?: "" }) +public fun MutableValidatedValue.asUShortHex(): MutableValidatedValue = lens(get = { it.toUShortOrNull(16) }, set = { it?.toString(16) ?: "" }) @JvmName("writableStringAsIntHexValidatedValue") -fun MutableValidatedValue.asIntHex(): MutableValidatedValue = lens(get = { it.toIntOrNull(16) }, set = { it?.toString(16) ?: "" }) +public fun MutableValidatedValue.asIntHex(): MutableValidatedValue = lens(get = { it.toIntOrNull(16) }, set = { it?.toString(16) ?: "" }) @JvmName("writableStringAsUIntHexValidatedValue") -fun MutableValidatedValue.asUIntHex(): MutableValidatedValue = lens(get = { it.toUIntOrNull(16) }, set = { it?.toString(16) ?: "" }) +public fun MutableValidatedValue.asUIntHex(): MutableValidatedValue = lens(get = { it.toUIntOrNull(16) }, set = { it?.toString(16) ?: "" }) @JvmName("writableStringAsLongHexValidatedValue") -fun MutableValidatedValue.asLongHex(): MutableValidatedValue = lens(get = { it.toLongOrNull(16) }, set = { it?.toString(16) ?: "" }) +public fun MutableValidatedValue.asLongHex(): MutableValidatedValue = lens(get = { it.toLongOrNull(16) }, set = { it?.toString(16) ?: "" }) @JvmName("writableStringAsULongHexValidatedValue") -fun MutableValidatedValue.asULongHex(): MutableValidatedValue = lens(get = { it.toULongOrNull(16) }, set = { it?.toString(16) ?: "" }) +public fun MutableValidatedValue.asULongHex(): MutableValidatedValue = lens(get = { it.toULongOrNull(16) }, set = { it?.toString(16) ?: "" }) @JvmName("writableIntAsDoubleNullableValidatedValue") -fun MutableValidatedValue.asDouble(): MutableValidatedValue = lens(get = { it?.toDouble() }, set = { it?.toInt() }) +public fun MutableValidatedValue.asDouble(): MutableValidatedValue = lens(get = { it?.toDouble() }, set = { it?.toInt() }) diff --git a/src/commonMain/kotlin/com/lightningkite/reactive/extensions/debounce.kt b/src/commonMain/kotlin/com/lightningkite/reactive/extensions/debounce.kt index dc1d182..6c0a0c4 100644 --- a/src/commonMain/kotlin/com/lightningkite/reactive/extensions/debounce.kt +++ b/src/commonMain/kotlin/com/lightningkite/reactive/extensions/debounce.kt @@ -32,10 +32,10 @@ import kotlin.time.Duration.Companion.milliseconds * * @see DebounceListenable */ -class DebounceReactive( - val source: Reactive, - val scope: CoroutineScope, - val duration: Duration +public class DebounceReactive( + public val source: Reactive, + public val scope: CoroutineScope, + public val duration: Duration ) : Reactive, Listenable by DebounceListenable(source, scope, duration) { override val state: ReactiveState get() = source.state } @@ -60,7 +60,7 @@ class DebounceReactive( * * @see DebounceReactive */ -class DebounceListenable(val source: Listenable, val scope: CoroutineScope, val duration: Duration) : BaseListenable() { +public class DebounceListenable(public val source: Listenable, public val scope: CoroutineScope, public val duration: Duration) : BaseListenable() { @Volatile private var changeCount = 0 @@ -87,25 +87,25 @@ class DebounceListenable(val source: Listenable, val scope: CoroutineScope, val * Debounces listener notifications by [timeMs] milliseconds. State is always current. * @see DebounceReactive */ -fun Reactive.debounce(timeMs: Long, scope: CoroutineScope): Reactive = DebounceReactive(this, scope, timeMs.milliseconds) +public fun Reactive.debounce(timeMs: Long, scope: CoroutineScope): Reactive = DebounceReactive(this, scope, timeMs.milliseconds) /** * Debounces listener notifications by [duration]. State is always current. * @see DebounceReactive */ -fun Reactive.debounce(duration: Duration, scope: CoroutineScope): Reactive = DebounceReactive(this, scope, duration) +public fun Reactive.debounce(duration: Duration, scope: CoroutineScope): Reactive = DebounceReactive(this, scope, duration) /** * Debounces listener notifications by [timeMs] milliseconds. * @see DebounceListenable */ -fun Listenable.debounce(timeMs: Long, scope: CoroutineScope): Listenable = DebounceListenable(this, scope, timeMs.milliseconds) +public fun Listenable.debounce(timeMs: Long, scope: CoroutineScope): Listenable = DebounceListenable(this, scope, timeMs.milliseconds) /** * Debounces listener notifications by [duration]. * @see DebounceListenable */ -fun Listenable.debounce(duration: Duration, scope: CoroutineScope): Listenable = DebounceListenable(this, scope, duration) +public fun Listenable.debounce(duration: Duration, scope: CoroutineScope): Listenable = DebounceListenable(this, scope, duration) /** * Debounces write operations to this [MutableReactive]. @@ -119,7 +119,7 @@ fun Listenable.debounce(duration: Duration, scope: CoroutineScope): Listenable = * @param duration The debounce delay for write operations. * @return A [MutableReactive] wrapper with debounced writes. */ -fun MutableReactive.debounceWrite(duration: Duration): MutableReactive = object: MutableReactive by this { +public fun MutableReactive.debounceWrite(duration: Duration): MutableReactive = object: MutableReactive by this { @Volatile var setIndex = 0 diff --git a/src/commonMain/kotlin/com/lightningkite/reactive/extensions/helpers.kt b/src/commonMain/kotlin/com/lightningkite/reactive/extensions/helpers.kt index 1aae25a..cd698f5 100644 --- a/src/commonMain/kotlin/com/lightningkite/reactive/extensions/helpers.kt +++ b/src/commonMain/kotlin/com/lightningkite/reactive/extensions/helpers.kt @@ -24,8 +24,8 @@ import kotlin.jvm.JvmName @JsName("invokeAllSafeMutable") @JvmName("invokeAllSafeMutable") -fun MutableList<() -> Unit>.invokeAllSafe() = toList().invokeAllSafe() -fun List<() -> Unit>.invokeAllSafe() = forEach { +public fun MutableList<() -> Unit>.invokeAllSafe(): Unit = toList().invokeAllSafe() +public fun List<() -> Unit>.invokeAllSafe(): Unit = forEach { try { it() } catch (e: Exception) { @@ -34,7 +34,7 @@ fun List<() -> Unit>.invokeAllSafe() = forEach { } } -var MutableValue.value: T +public var MutableValue.value: T @Deprecated("This is syntax sugar for SETTING values. Retrieving will always throw an exception.", level = DeprecationLevel.ERROR) get() = throw IllegalStateException("Attempted to retrieve value for set-only property") @JvmName("setValue2") @@ -42,7 +42,7 @@ var MutableValue.value: T valueSet(value) } -operator fun Listenable.plus(other: Listenable): Listenable = object: Listenable { +public operator fun Listenable.plus(other: Listenable): Listenable = object: Listenable { override fun addListener(listener: () -> Unit): Release { val a = this@plus.addListener(listener) val b = other.addListener(listener) @@ -53,14 +53,14 @@ operator fun Listenable.plus(other: Listenable): Listenable = object: Listenable } } -fun Reactive.withWrite(action: suspend Reactive.(T) -> Unit): MutableReactive = +public fun Reactive.withWrite(action: suspend Reactive.(T) -> Unit): MutableReactive = object : MutableReactive, Reactive by this { override suspend fun set(value: T) { action(this@withWrite, value) } } -fun Reactive.onNextSuccess(action: (T) -> Unit): Release? { +public fun Reactive.onNextSuccess(action: (T) -> Unit): Release? { // A successful state is one somebody is maintaining, so it needs no subscription at all. if (state.success) { state.onSuccess(action) @@ -82,28 +82,28 @@ fun Reactive.onNextSuccess(action: (T) -> Unit): Release? { return release } -fun MutableReactive.nullable(): MutableReactive = +public fun MutableReactive.nullable(): MutableReactive = object : MutableReactive, Reactive by this { override suspend fun set(value: T?) { if (value != null) this@nullable.set(value) } } -suspend infix fun MutableReactive.modify(action: suspend (T) -> T) { +public suspend infix fun MutableReactive.modify(action: suspend (T) -> T) { set(action(await())) } -suspend infix fun MutableReactiveValue.modify(action: suspend (T) -> T) { +public suspend infix fun MutableReactiveValue.modify(action: suspend (T) -> T) { value = action(value) } -suspend fun MutableReactive.toggle() { set(!awaitOnce()) } -fun MutableReactiveValue.toggle() { value = !value } +public suspend fun MutableReactive.toggle() { set(!awaitOnce()) } +public fun MutableReactiveValue.toggle() { value = !value } /** * Starts using this [ResourceUse] and tracks it as a dependency in future loops. * */ -fun DependencyTracker.use(resourceUse: ResourceUse) { +public fun DependencyTracker.use(resourceUse: ResourceUse) { if (existingDependency(resourceUse) == null) { registerDependency(resourceUse, resourceUse.beginUse()) } @@ -116,7 +116,7 @@ fun DependencyTracker.use(resourceUse: ResourceUse) { * If this scope contains a [DependencyChangeListener] then the resource use * is attached as a dependency. * */ -fun CoroutineScope.use(resourceUse: ResourceUse) { +public fun CoroutineScope.use(resourceUse: ResourceUse) { coroutineContext[DependencyChangeListener.Key]?.let { it.use(resourceUse) return @@ -125,23 +125,23 @@ fun CoroutineScope.use(resourceUse: ResourceUse) { resourceUse.beginUse().also(::onRemove) } -fun > WRITE.interceptWrite(action: suspend WRITE.(T) -> Unit): MutableReactive = +public fun > WRITE.interceptWrite(action: suspend WRITE.(T) -> Unit): MutableReactive = object : MutableReactive, Reactive by this { override suspend fun set(value: T) { action(this@interceptWrite, value) } } -fun Reactive>.flatten(): Reactive = remember { this@flatten()() } +public fun Reactive>.flatten(): Reactive = remember { this@flatten()() } -fun Reactive>.flatten(): MutableReactive = +public fun Reactive>.flatten(): MutableReactive = remember { this@flatten()() }.withWrite { // awaitOnce rather than reading state: if the outer reactive is lazy it has no value to // read unless something is listening, and the write would be silently dropped. this@flatten.awaitOnce().set(it) } -fun CoroutineScope.asyncReactive(action: suspend () -> T): Reactive { +public fun CoroutineScope.asyncReactive(action: suspend () -> T): Reactive { val prop = LateInitSignal() launch { prop.value = action() @@ -150,7 +150,7 @@ fun CoroutineScope.asyncReactive(action: suspend () -> T): Reactive { } @OptIn(ExperimentalCoroutinesApi::class) -fun Deferred.toReactive() = object : BaseReactive() { +public fun Deferred.toReactive(): Reactive = object : BaseReactive() { init { this@toReactive[Job]?.invokeOnCompletion { state = if (it == null) ReactiveState(getCompleted()) else ReactiveState.exception(it as? Exception ?: Exception("Must be exception, not throwable", it)) @@ -158,15 +158,15 @@ fun Deferred.toReactive() = object : BaseReactive() { } } -suspend operator fun (ReactiveContext.()->R).invoke(): R { +public suspend operator fun (ReactiveContext.()->R).invoke(): R { return remember { this@invoke() }.awaitOnce() } -suspend operator fun (ReactiveContext.(A)->R).invoke(a: A): R { +public suspend operator fun (ReactiveContext.(A)->R).invoke(a: A): R { return remember { this@invoke(a) }.awaitOnce() } -suspend operator fun (ReactiveContext.(A, B)->R).invoke(a: A, b: B): R { +public suspend operator fun (ReactiveContext.(A, B)->R).invoke(a: A, b: B): R { return remember { this@invoke(a, b) }.awaitOnce() } -suspend operator fun (ReactiveContext.(A, B, C)->R).invoke(a: A, b: B, c: C): R { +public suspend operator fun (ReactiveContext.(A, B, C)->R).invoke(a: A, b: B, c: C): R { return remember { this@invoke(a, b, c) }.awaitOnce() } \ No newline at end of file diff --git a/src/commonMain/kotlin/com/lightningkite/reactive/lensing/LensByElement.kt b/src/commonMain/kotlin/com/lightningkite/reactive/lensing/LensByElement.kt index 88fe020..a6462e9 100644 --- a/src/commonMain/kotlin/com/lightningkite/reactive/lensing/LensByElement.kt +++ b/src/commonMain/kotlin/com/lightningkite/reactive/lensing/LensByElement.kt @@ -14,42 +14,43 @@ import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job import kotlin.collections.plus +import kotlin.coroutines.CoroutineContext import kotlin.coroutines.cancellation.CancellationException import kotlin.jvm.JvmName -fun MutableReactive>.lensByElementWithIdentity( +public fun MutableReactive>.lensByElementWithIdentity( identity: (E) -> ID, map: CoroutineScope.(MutableWithReactiveValue) -> W -) = LensByElement(this, identity = identity, elementLens = { it.map(it) }) +): LensByElement = LensByElement(this, identity = identity, elementLens = { it.map(it) }) -fun MutableReactive>.lensByElementWithIdentity( +public fun MutableReactive>.lensByElementWithIdentity( identity: (E) -> ID -) = LensElements(this, identity = identity, elementLens = { it }) +): LensElements = LensElements(this, identity = identity, elementLens = { it }) @JvmName("setLensByElementWithIdentity") @Suppress("Deprecation") -fun MutableReactive>.lensByElementWithIdentity( +public fun MutableReactive>.lensByElementWithIdentity( identity: (E) -> ID, map: CoroutineScope.(MutableWithReactiveValue) -> W -) = lens(get = { it.toList() }, set = { it.toSet() }).lensByElement(identity, map) +): LensByElement = lens(get = { it.toList() }, set = { it.toSet() }).lensByElement(identity, map) @JvmName("setLensByElementWithIdentity") @Suppress("Deprecation") -fun MutableReactive>.lensByElementWithIdentity( +public fun MutableReactive>.lensByElementWithIdentity( identity: (E) -> ID -) = lens(get = { it.toList() }, set = { it.toSet() }).lensByElement(identity) +): LensElements = lens(get = { it.toList() }, set = { it.toSet() }).lensByElement(identity) -typealias LensElements = LensByElement.Element> +public typealias LensElements = LensByElement.Element> -class LensByElement( - val source: MutableReactive>, - val identity: (E) -> ID, - val elementLens: (LensByElement.Element) -> T +public class LensByElement( + public val source: MutableReactive>, + public val identity: (E) -> ID, + public val elementLens: (LensByElement.Element) -> T ) : Reactive> { private val node = IssueNode(parent = (source as? MutableValidated)?.node).apply { connect() } - inner class Element internal constructor(valueInit: E) : MutableWithReactiveValue, MutableValidated, CoroutineScope { + public inner class Element internal constructor(valueInit: E) : MutableWithReactiveValue, MutableValidated, CoroutineScope { override val node: IssueNode = this@LensByElement.node.child() private var job = Job() @@ -58,7 +59,7 @@ class LensByElement( Reactive.reportException(throwable) } } - override val coroutineContext get() = restOfContext + job + override val coroutineContext: CoroutineContext get() = restOfContext + job internal var dead = false set(value) { @@ -68,7 +69,7 @@ class LensByElement( job.cancel() job = Job() } - var id: ID = identity(valueInit) + public var id: ID = identity(valueInit) private set private val listeners = ArrayList<() -> Unit>() override var value: E = valueInit @@ -114,10 +115,10 @@ class LensByElement( } } - val view = elementLens(this) + public val view: T = elementLens(this) } - inner class Elements : MutableReactive> { + public inner class Elements : MutableReactive> { override suspend fun set(value: List) { source.set(value.map { it.queuedOrValue }) } @@ -178,22 +179,22 @@ class LensByElement( } } - val elements = Elements() + public val elements: Elements = Elements() - fun newElement(e: E): Element = Element(e) - suspend fun add(index: Int, value: E): T { + public fun newElement(e: E): Element = Element(e) + public suspend fun add(index: Int, value: E): T { val newly = newElement(value) elements.set(elements.awaitOnce().toMutableList().apply { add(index, newly) }) return newly.view } - suspend fun add(value: E): T { + public suspend fun add(value: E): T { val newly = newElement(value) elements.set(elements.awaitOnce() + newly) return newly.view } - suspend fun upsert(value: E): T { + public suspend fun upsert(value: E): T { val id = identity(value) val existing = elements.awaitOnce().find { it.id == id } return if (existing == null) add(value) else { @@ -202,12 +203,12 @@ class LensByElement( } } - suspend fun remove(element: E) { + public suspend fun remove(element: E) { val id = identity(element) removeById(id) } - suspend fun removeById(id: ID) { + public suspend fun removeById(id: ID) { elements.set(elements.awaitOnce().filter { it.id != id }) } @@ -219,21 +220,21 @@ class LensByElement( @Deprecated("Be specific about what kind you need.") -fun MutableReactive>.lensByElement(identity: (E) -> ID, map: CoroutineScope.(MutableWithReactiveValue) -> W) = +public fun MutableReactive>.lensByElement(identity: (E) -> ID, map: CoroutineScope.(MutableWithReactiveValue) -> W): LensByElement = LensByElement(this, identity = identity, elementLens = { it.map(it) }) @Deprecated("Be specific about what kind you need.") -fun MutableReactive>.lensByElement(identity: (E) -> ID) = +public fun MutableReactive>.lensByElement(identity: (E) -> ID): LensElements = LensElements(this, identity = identity, elementLens = { it }) @Deprecated("Be specific about what kind you need.") @JvmName("setLensByElement") @Suppress("Deprecation") -fun MutableReactive>.lensByElement(identity: (E) -> ID, map: CoroutineScope.(MutableWithReactiveValue) -> W) = +public fun MutableReactive>.lensByElement(identity: (E) -> ID, map: CoroutineScope.(MutableWithReactiveValue) -> W): LensByElement = lens(get = { it.toList() }, set = { it.toSet() }).lensByElement(identity, map) @Deprecated("Be specific about what kind you need.") @JvmName("setLensByElement") @Suppress("Deprecation") -fun MutableReactive>.lensByElement(identity: (E) -> ID) = +public fun MutableReactive>.lensByElement(identity: (E) -> ID): LensElements = lens(get = { it.toList() }, set = { it.toSet() }).lensByElement(identity) diff --git a/src/commonMain/kotlin/com/lightningkite/reactive/lensing/LensByElementAssumingSetNeverManipulates.kt b/src/commonMain/kotlin/com/lightningkite/reactive/lensing/LensByElementAssumingSetNeverManipulates.kt index 58f8fe3..9ab154f 100644 --- a/src/commonMain/kotlin/com/lightningkite/reactive/lensing/LensByElementAssumingSetNeverManipulates.kt +++ b/src/commonMain/kotlin/com/lightningkite/reactive/lensing/LensByElementAssumingSetNeverManipulates.kt @@ -13,20 +13,20 @@ import kotlinx.coroutines.Job import kotlinx.coroutines.cancel -interface MutableReactiveElement : MutableWithReactiveValue { - val index: ReactiveValue +public interface MutableReactiveElement : MutableWithReactiveValue { + public val index: ReactiveValue } /** * THIS ONLY WORKS IF THE `set` on the receiver *never* manipulates the input before notifying. */ -fun MutableReactive>.lensByElementAssumingSetNeverManipulates(): Reactive>> = +public fun MutableReactive>.lensByElementAssumingSetNeverManipulates(): Reactive>> = lensByElementAssumingSetNeverManipulates { it } /** * THIS ONLY WORKS IF THE `set` on the receiver *never* manipulates the input before notifying. */ -fun MutableReactive>.lensByElementAssumingSetNeverManipulates(map: CoroutineScope.(MutableReactiveElement) -> W): Reactive> = +public fun MutableReactive>.lensByElementAssumingSetNeverManipulates(map: CoroutineScope.(MutableReactiveElement) -> W): Reactive> = LensByElementAssumingSetNeverManipulates(this, map) diff --git a/src/commonMain/kotlin/com/lightningkite/reactive/lensing/abstracts.kt b/src/commonMain/kotlin/com/lightningkite/reactive/lensing/abstracts.kt index b489bae..a921c2a 100644 --- a/src/commonMain/kotlin/com/lightningkite/reactive/lensing/abstracts.kt +++ b/src/commonMain/kotlin/com/lightningkite/reactive/lensing/abstracts.kt @@ -10,7 +10,7 @@ import com.lightningkite.reactive.core.Reactive import com.lightningkite.reactive.core.ReactiveState import com.lightningkite.reactive.core.ReactiveValue -open class Lens, T, L>(val source: S, val get: (T) -> L) : BaseReactive() { +public open class Lens, T, L>(public val source: S, public val get: (T) -> L) : BaseReactive() { override var state: ReactiveState get() { if (myListen == null) super.state = source.state.map(get) @@ -40,9 +40,9 @@ open class Lens, T, L>(val source: S, val get: (T) -> L) : BaseR } } -open class SetLens( +public open class SetLens( source: MutableReactive, get: (O) -> T, - val set: (T) -> O + public val set: (T) -> O ) : Lens, O, T>(source, get), MutableReactive { override suspend fun set(value: T) { val transformed = set.invoke(value) @@ -51,10 +51,10 @@ open class SetLens( } } -open class ModifyLens( +public open class ModifyLens( source: MutableReactive, get: (O) -> T, - val modify: (O, T) -> O + public val modify: (O, T) -> O ) : Lens, O, T>(source, get), MutableReactive { override suspend fun set(value: T) { val transformed = modify(source.awaitOnce(), value) @@ -63,9 +63,9 @@ open class ModifyLens( } } -open class ValueLens, T, L>( - val source: S, - val get: (T) -> L +public open class ValueLens, T, L>( + public val source: S, + public val get: (T) -> L ) : BaseReactiveValue(source.value.let(get)) { override var value: L get() { @@ -94,7 +94,7 @@ open class ValueLens, T, L>( } } -open class SetValueLens(source: MutableReactiveValue, get: (O) -> T, val set: (T) -> O) : +public open class SetValueLens(source: MutableReactiveValue, get: (O) -> T, public val set: (T) -> O) : ValueLens, O, T>(source, get), MutableReactiveValue { override var value: T get() = super.value @@ -105,7 +105,7 @@ open class SetValueLens(source: MutableReactiveValue, get: (O) -> T, va } } -open class ModifyValueLens(source: MutableReactiveValue, get: (O) -> T, val modify: (O, T) -> O) : +public open class ModifyValueLens(source: MutableReactiveValue, get: (O) -> T, public val modify: (O, T) -> O) : ValueLens, O, T>(source, get), MutableReactiveValue { override var value: T get() = super.value @@ -116,10 +116,10 @@ open class ModifyValueLens(source: MutableReactiveValue, get: (O) -> T, } } -fun Reactive.lens(get: (T) -> L): Reactive = Lens(this, get) -fun ReactiveValue.lens(get: (T) -> L): ReactiveValue = ValueLens(this, get) +public fun Reactive.lens(get: (T) -> L): Reactive = Lens(this, get) +public fun ReactiveValue.lens(get: (T) -> L): ReactiveValue = ValueLens(this, get) -fun Listenable.lensListenable( +public fun Listenable.lensListenable( get: () -> T ): Reactive = ValueLens( object: ReactiveValue, Listenable by this { diff --git a/src/commonMain/kotlin/com/lightningkite/reactive/lensing/validation/IssueNode.kt b/src/commonMain/kotlin/com/lightningkite/reactive/lensing/validation/IssueNode.kt index b8d91ba..64a2e59 100644 --- a/src/commonMain/kotlin/com/lightningkite/reactive/lensing/validation/IssueNode.kt +++ b/src/commonMain/kotlin/com/lightningkite/reactive/lensing/validation/IssueNode.kt @@ -37,11 +37,11 @@ import com.lightningkite.reactive.core.remember * * @property parent The parent node in the validation tree, or null if this is the root. */ -class IssueNode(val parent: IssueNode? = null) : ResourceUse { +public class IssueNode(public val parent: IssueNode? = null) : ResourceUse { private val nodeIssue = Signal>(Constant(null)) - fun report(issue: Issue?) { nodeIssue.value = Constant(issue) } - fun reactiveReport(issue: ReactiveContext.() -> Issue?) { + public fun report(issue: Issue?) { nodeIssue.value = Constant(issue) } + public fun reactiveReport(issue: ReactiveContext.() -> Issue?) { nodeIssue.value = remember(action = issue) } @@ -58,7 +58,7 @@ class IssueNode(val parent: IssueNode? = null) : ResourceUse { * Instead, consider using [child] outside of the [ReactiveContext], and then report to that outside * node inside any reactive code. * */ - fun child() = IssueNode(this).apply { connect() } + public fun child(): IssueNode = IssueNode(this).apply { connect() } private var connected = false @@ -68,7 +68,7 @@ class IssueNode(val parent: IssueNode? = null) : ResourceUse { * * Useful for establishing validation dependencies once a set of data has become relevant. * */ - fun connect() { + public fun connect() { if (connected || parent == null) return connected = true parent.children.add(this) @@ -79,7 +79,7 @@ class IssueNode(val parent: IssueNode? = null) : ResourceUse { * * Useful for removing validation dependencies on data that is no longer relevant. * */ - fun disconnect() { + public fun disconnect() { if (!connected || parent == null) return connected = false parent.children.remove(this) @@ -90,7 +90,7 @@ class IssueNode(val parent: IssueNode? = null) : ResourceUse { return ::disconnect } - val issues : Reactive> = remember { + public val issues : Reactive> = remember { listOfNotNull(nodeIssue()()) + children().flatMap { it.issues() } } } @@ -98,16 +98,16 @@ class IssueNode(val parent: IssueNode? = null) : ResourceUse { /** * Represents a validation issue, which can be either a warning or an invalid state. */ -sealed interface Issue { - val summary: String - val description: String +public sealed interface Issue { + public val summary: String + public val description: String /** * Represents a warning issue. Does not necessarily prevent usage, but should be addressed. * * Values that result in an [Issue.Warning] being reported will still be used. */ - data class Warning( + public data class Warning( override val summary: String, override val description: String = summary ) : Issue @@ -118,7 +118,7 @@ sealed interface Issue { * Values that result in an [Issue.Invalid] being reported will be **discarded**. * I.e., if a lensed child of a [MutableValidated] reports [Issue.Invalid] on a value, it will not modify its parent. */ - data class Invalid( + public data class Invalid( override val summary : String, override val description: String = summary ) : Issue diff --git a/src/commonMain/kotlin/com/lightningkite/reactive/lensing/validation/Validated.kt b/src/commonMain/kotlin/com/lightningkite/reactive/lensing/validation/Validated.kt index a838710..48f0b4c 100644 --- a/src/commonMain/kotlin/com/lightningkite/reactive/lensing/validation/Validated.kt +++ b/src/commonMain/kotlin/com/lightningkite/reactive/lensing/validation/Validated.kt @@ -35,14 +35,14 @@ import com.lightningkite.reactive.core.ReactiveValue * * @see Issue */ -interface IssueTracking { - val node: IssueNode +public interface IssueTracking { + public val node: IssueNode } /** * Returns the current list of issues, this includes issues from this node and any child nodes. */ -val IssueTracking.issues get() = node.issues +public val IssueTracking.issues: Reactive> get() = node.issues /** * Reports a new issue to this node. @@ -51,7 +51,7 @@ val IssueTracking.issues get() = node.issues * * @param issue The issue to report, or null to clear this node's issue. */ -fun IssueTracking.report(issue: Issue?) = node.report(issue) +public fun IssueTracking.report(issue: Issue?): Unit = node.report(issue) /** * Represents a validated reactive value. See [IssueTracking] for more details about @@ -60,7 +60,7 @@ fun IssueTracking.report(issue: Issue?) = node.report(issue) * @see IssueTracking * @see Reactive */ -interface Validated : IssueTracking, Reactive +public interface Validated : IssueTracking, Reactive /** * Represents a validated reactive value with direct value access. See [IssueTracking] for more details about @@ -69,7 +69,7 @@ interface Validated : IssueTracking, Reactive * @see IssueTracking * @see ReactiveValue */ -interface ValidatedValue : IssueTracking, ReactiveValue, Validated +public interface ValidatedValue : IssueTracking, ReactiveValue, Validated /** * Represents a mutable validated reactive value. @@ -77,7 +77,7 @@ interface ValidatedValue : IssueTracking, ReactiveValue, Validated * Lensing a [MutableValidated] creates a child of this node in the validation tree. * See [IssueTracking] for more details about validation trees. */ -interface MutableValidated : IssueTracking, MutableReactive, Validated { +public interface MutableValidated : IssueTracking, MutableReactive, Validated { /** * Creates a transforming lens for type conversion. * Returns a [MutableValidated] for the sub-value, preserving validation. @@ -112,7 +112,7 @@ interface MutableValidated : IssueTracking, MutableReactive, Validated * @see IssueTracking * @see MutableReactiveValue */ -interface MutableValidatedValue : IssueTracking, MutableReactiveValue, ValidatedValue, MutableValidated { +public interface MutableValidatedValue : IssueTracking, MutableReactiveValue, ValidatedValue, MutableValidated { /** * Creates a transforming lens for type conversion. * Returns a [MutableValidatedValue] for the sub-value, preserving validation. diff --git a/src/commonMain/kotlin/com/lightningkite/reactive/lensing/validation/ValidatedDraft.kt b/src/commonMain/kotlin/com/lightningkite/reactive/lensing/validation/ValidatedDraft.kt index 8e82b35..88851a0 100644 --- a/src/commonMain/kotlin/com/lightningkite/reactive/lensing/validation/ValidatedDraft.kt +++ b/src/commonMain/kotlin/com/lightningkite/reactive/lensing/validation/ValidatedDraft.kt @@ -8,7 +8,7 @@ import com.lightningkite.reactive.core.Draft * @see Draft * @see MutableValidated * */ -interface ValidatedDraft : Draft, MutableValidated +public interface ValidatedDraft : Draft, MutableValidated private class RootValidatedDraft(val draft: Draft, reportTo: IssueNode? = null) : ValidatedDraft, Draft by draft { override val node: IssueNode = reportTo?.child() ?: IssueNode() @@ -17,4 +17,4 @@ private class RootValidatedDraft(val draft: Draft, reportTo: IssueNode? = override fun lens(get: (T) -> L, modify: (T, L) -> T): MutableValidated = ValidatedModifyLens(this, get, modify) } -fun Draft.validated(reportTo: IssueNode? = null): ValidatedDraft = RootValidatedDraft(this, reportTo) \ No newline at end of file +public fun Draft.validated(reportTo: IssueNode? = null): ValidatedDraft = RootValidatedDraft(this, reportTo) \ No newline at end of file diff --git a/src/commonMain/kotlin/com/lightningkite/reactive/lensing/validation/entrypoints.kt b/src/commonMain/kotlin/com/lightningkite/reactive/lensing/validation/entrypoints.kt index 494b900..736284b 100644 --- a/src/commonMain/kotlin/com/lightningkite/reactive/lensing/validation/entrypoints.kt +++ b/src/commonMain/kotlin/com/lightningkite/reactive/lensing/validation/entrypoints.kt @@ -48,7 +48,7 @@ private class RootMutableValidatedValue( * * @see IssueTracking */ -fun Reactive.validated(reportTo: IssueNode? = null): Validated = this as? Validated ?: RootValidated(this, reportTo) +public fun Reactive.validated(reportTo: IssueNode? = null): Validated = this as? Validated ?: RootValidated(this, reportTo) /** * Wraps a [MutableReactive] as a [MutableValidated]. @@ -61,7 +61,7 @@ fun Reactive.validated(reportTo: IssueNode? = null): Validated = this * * @see IssueTracking */ -fun MutableReactive.validated(reportTo: IssueNode? = null): MutableValidated = this as? MutableValidated ?: RootMutableValidated(this, reportTo) +public fun MutableReactive.validated(reportTo: IssueNode? = null): MutableValidated = this as? MutableValidated ?: RootMutableValidated(this, reportTo) /** * Wraps a [ReactiveValue] as a [ValidatedValue]. @@ -74,7 +74,7 @@ fun MutableReactive.validated(reportTo: IssueNode? = null): MutableValida * * @see IssueTracking */ -fun ReactiveValue.validated(reportTo: IssueNode? = null): ValidatedValue = this as? ValidatedValue ?: RootValidatedValue(this, reportTo) +public fun ReactiveValue.validated(reportTo: IssueNode? = null): ValidatedValue = this as? ValidatedValue ?: RootValidatedValue(this, reportTo) /** * Wraps a [MutableReactiveValue] as a [MutableValidatedValue]. @@ -87,4 +87,4 @@ fun ReactiveValue.validated(reportTo: IssueNode? = null): ValidatedValue< * * @see IssueTracking */ -fun MutableReactiveValue.validated(reportTo: IssueNode? = null): MutableValidatedValue = this as? MutableValidatedValue ?: RootMutableValidatedValue(this, reportTo) \ No newline at end of file +public fun MutableReactiveValue.validated(reportTo: IssueNode? = null): MutableValidatedValue = this as? MutableValidatedValue ?: RootMutableValidatedValue(this, reportTo) \ No newline at end of file diff --git a/src/commonMain/kotlin/com/lightningkite/reactive/lensing/validation/helpers.kt b/src/commonMain/kotlin/com/lightningkite/reactive/lensing/validation/helpers.kt index 1607315..c5322bf 100644 --- a/src/commonMain/kotlin/com/lightningkite/reactive/lensing/validation/helpers.kt +++ b/src/commonMain/kotlin/com/lightningkite/reactive/lensing/validation/helpers.kt @@ -14,10 +14,10 @@ import com.lightningkite.reactive.core.Signal * @param setOnIssue If true, issues are reported as [Issue.Warning]; if false, as [Issue.Invalid]. * @param validate Function that returns a string issue message or null for valid values. */ -fun MutableValidated.validate( +public fun MutableValidated.validate( setOnIssue: Boolean = true, validate: (T) -> String? -) = checkForIssue { value -> +): MutableValidated = checkForIssue { value -> validate(value)?.let { if (setOnIssue) Issue.Warning(it) else Issue.Invalid(it) @@ -33,10 +33,10 @@ fun MutableValidated.validate( * @param setOnIssue If true, issues are reported as [Issue.Warning]; if false, as [Issue.Invalid]. * @param validate Function that returns a string issue message or null for valid values. */ -fun MutableValidatedValue.validate( +public fun MutableValidatedValue.validate( setOnIssue: Boolean = true, validate: (T) -> String? -) = checkForIssue { value -> +): MutableValidatedValue = checkForIssue { value -> validate(value)?.let { if (setOnIssue) Issue.Warning(it) else Issue.Invalid(it) @@ -51,12 +51,12 @@ fun MutableValidatedValue.validate( * @param setOnIssue If true, issues are reported as [Issue.Warning]; if false, as [Issue.Invalid]. * @param condition Function that returns true if valid, false if invalid. */ -fun MutableValidated.assert( +public fun MutableValidated.assert( summary: String, description: String = summary, setOnIssue: Boolean = true, condition: (T) -> Boolean -) = checkForIssue { +): MutableValidated = checkForIssue { if (condition(it)) return@checkForIssue null if (setOnIssue) Issue.Warning(summary, description) @@ -71,12 +71,12 @@ fun MutableValidated.assert( * @param setOnIssue If true, issues are reported as [Issue.Warning]; if false, as [Issue.Invalid]. * @param condition Function that returns true if valid, false if invalid. */ -fun MutableValidatedValue.assert( +public fun MutableValidatedValue.assert( summary: String, description: String = summary, setOnIssue: Boolean = true, condition: (T) -> Boolean -) = checkForIssue { +): MutableValidatedValue = checkForIssue { if (condition(it)) return@checkForIssue null if (setOnIssue) Issue.Warning(summary, description) @@ -90,11 +90,11 @@ fun MutableValidatedValue.assert( * @param description Detailed description of the issue (defaults to [summary]). * @param setOnIssue If true, issues are reported as [Issue.Warning]; if false, as [Issue.Invalid]. */ -fun MutableValidated.assertNotNull( +public fun MutableValidated.assertNotNull( summary: String = "Cannot be blank", description: String = summary, setOnIssue: Boolean = true -) = assert(summary, description, setOnIssue) { it != null } +): MutableValidated = assert(summary, description, setOnIssue) { it != null } /** * Validates that the value of this [MutableValidatedValue] is not null. @@ -103,11 +103,11 @@ fun MutableValidated.assertNotNull( * @param description Detailed description of the issue (defaults to [summary]). * @param setOnIssue If true, issues are reported as [Issue.Warning]; if false, as [Issue.Invalid]. */ -fun MutableValidatedValue.assertNotNull( +public fun MutableValidatedValue.assertNotNull( summary: String = "Cannot be blank", description: String = summary, setOnIssue: Boolean = true -) = assert(summary, description, setOnIssue) { it != null } +): MutableValidatedValue = assert(summary, description, setOnIssue) { it != null } /** * Validates that the value of this [MutableValidated] is not blank. @@ -116,11 +116,11 @@ fun MutableValidatedValue.assertNotNull( * @param description Detailed description of the issue (defaults to [summary]). * @param setOnIssue If true, issues are reported as [Issue.Warning]; if false, as [Issue.Invalid]. */ -fun MutableValidated.assertNotBlank( +public fun MutableValidated.assertNotBlank( summary: String = "Cannot be blank", description: String = summary, setOnIssue: Boolean = true -) = assert(summary, description, setOnIssue) { it.isNotBlank() } +): MutableValidated = assert(summary, description, setOnIssue) { it.isNotBlank() } /** * Validates that the value of this [MutableValidatedValue] is not blank. @@ -129,11 +129,11 @@ fun MutableValidated.assertNotBlank( * @param description Detailed description of the issue (defaults to [summary]). * @param setOnIssue If true, issues are reported as [Issue.Warning]; if false, as [Issue.Invalid]. */ -fun MutableValidatedValue.assertNotBlank( +public fun MutableValidatedValue.assertNotBlank( summary: String = "Cannot be blank", description: String = summary, setOnIssue: Boolean = true -) = assert(summary, description, setOnIssue) { it.isNotBlank() } +): MutableValidatedValue = assert(summary, description, setOnIssue) { it.isNotBlank() } /** * Adds a validation check to this [MutableReactive] instance. @@ -144,10 +144,10 @@ fun MutableValidatedValue.assertNotBlank( * @param setOnIssue If true, issues are reported as [Issue.Warning]; if false, as [Issue.Invalid]. * @param validate Function that returns a string issue message or null for valid values. */ -fun MutableReactive.validate( +public fun MutableReactive.validate( setOnIssue: Boolean = true, validate: (T) -> String? -) = checkForIssue { value -> +): MutableValidated = checkForIssue { value -> validate(value)?.let { if (setOnIssue) Issue.Warning(it) else Issue.Invalid(it) @@ -163,10 +163,10 @@ fun MutableReactive.validate( * @param setOnIssue If true, issues are reported as [Issue.Warning]; if false, as [Issue.Invalid]. * @param validate Function that returns a string issue message or null for valid values. */ -fun MutableReactiveValue.validate( +public fun MutableReactiveValue.validate( setOnIssue: Boolean = true, validate: (T) -> String? -) = checkForIssue { value -> +): MutableValidatedValue = checkForIssue { value -> validate(value)?.let { if (setOnIssue) Issue.Warning(it) else Issue.Invalid(it) @@ -181,12 +181,12 @@ fun MutableReactiveValue.validate( * @param setOnIssue If true, issues are reported as [Issue.Warning]; if false, as [Issue.Invalid]. * @param condition Function that returns true if valid, false if invalid. */ -fun MutableReactive.assert( +public fun MutableReactive.assert( summary: String, description: String = summary, setOnIssue: Boolean = true, condition: (T) -> Boolean -) = checkForIssue { +): MutableValidated = checkForIssue { if (condition(it)) return@checkForIssue null if (setOnIssue) Issue.Warning(summary, description) @@ -201,12 +201,12 @@ fun MutableReactive.assert( * @param setOnIssue If true, issues are reported as [Issue.Warning]; if false, as [Issue.Invalid]. * @param condition Function that returns true if valid, false if invalid. */ -fun MutableReactiveValue.assert( +public fun MutableReactiveValue.assert( summary: String, description: String = summary, setOnIssue: Boolean = true, condition: (T) -> Boolean -) = checkForIssue { +): MutableValidatedValue = checkForIssue { if (condition(it)) return@checkForIssue null if (setOnIssue) Issue.Warning(summary, description) @@ -220,11 +220,11 @@ fun MutableReactiveValue.assert( * @param description Detailed description of the issue (defaults to [summary]). * @param setOnIssue If true, issues are reported as [Issue.Warning]; if false, as [Issue.Invalid]. */ -fun MutableReactive.assertNotNull( +public fun MutableReactive.assertNotNull( summary: String = "Cannot be blank", description: String = summary, setOnIssue: Boolean = true -) = assert(summary, description, setOnIssue) { it != null } +): MutableValidated = assert(summary, description, setOnIssue) { it != null } /** * Validates that the value of this [MutableReactiveValue] is not null. @@ -233,11 +233,11 @@ fun MutableReactive.assertNotNull( * @param description Detailed description of the issue (defaults to [summary]). * @param setOnIssue If true, issues are reported as [Issue.Warning]; if false, as [Issue.Invalid]. */ -fun MutableReactiveValue.assertNotNull( +public fun MutableReactiveValue.assertNotNull( summary: String = "Cannot be blank", description: String = summary, setOnIssue: Boolean = true -) = assert(summary, description, setOnIssue) { it != null } +): MutableValidatedValue = assert(summary, description, setOnIssue) { it != null } /** * Validates that the value of this [MutableReactive] is not blank. @@ -246,11 +246,11 @@ fun MutableReactiveValue.assertNotNull( * @param description Detailed description of the issue (defaults to [summary]). * @param setOnIssue If true, issues are reported as [Issue.Warning]; if false, as [Issue.Invalid]. */ -fun MutableReactive.assertNotBlank( +public fun MutableReactive.assertNotBlank( summary: String = "Cannot be blank", description: String = summary, setOnIssue: Boolean = true -) = assert(summary, description, setOnIssue) { it.isNotBlank() } +): MutableValidated = assert(summary, description, setOnIssue) { it.isNotBlank() } /** * Validates that the value of this [MutableReactiveValue] is not blank. @@ -259,16 +259,16 @@ fun MutableReactive.assertNotBlank( * @param description Detailed description of the issue (defaults to [summary]). * @param setOnIssue If true, issues are reported as [Issue.Warning]; if false, as [Issue.Invalid]. */ -fun MutableReactiveValue.assertNotBlank( +public fun MutableReactiveValue.assertNotBlank( summary: String = "Cannot be blank", description: String = summary, setOnIssue: Boolean = true -) = assert(summary, description, setOnIssue) { it.isNotBlank() } +): MutableValidatedValue = assert(summary, description, setOnIssue) { it.isNotBlank() } /** * Runs the provided validation condition reactively, reporting to a child of this [IssueTracking] node. * */ -fun IssueTracking.report(issue: ReactiveContext.() -> Issue?) { +public fun IssueTracking.report(issue: ReactiveContext.() -> Issue?) { val child = IssueNode(parent = node) child.connect() child.reactiveReport(issue) @@ -277,13 +277,13 @@ fun IssueTracking.report(issue: ReactiveContext.() -> Issue?) { /** * Runs the provided validation condition reactively, reporting to a child of this [IssueTracking] node. * */ -fun Validated.validateReactive(issue: ReactiveContext.(T) -> String?) = report { issue(this@validateReactive.invoke())?.let(Issue::Warning) } +public fun Validated.validateReactive(issue: ReactiveContext.(T) -> String?): Unit = report { issue(this@validateReactive.invoke())?.let(Issue::Warning) } /** * Asserts the provided condition reactively, constructing and reporting an [Issue.Warning] to a child of this [IssueTracking] node. * */ -fun Validated.assertReactive( +public fun Validated.assertReactive( summary: String, description: String = summary, condition: ReactiveContext.(T) -> Boolean -) = report { if (condition(this@assertReactive.invoke())) null else Issue.Warning(summary, description) } \ No newline at end of file +): Unit = report { if (condition(this@assertReactive.invoke())) null else Issue.Warning(summary, description) } \ No newline at end of file diff --git a/src/commonMain/kotlin/com/lightningkite/reactive/lensing/validation/validation.kt b/src/commonMain/kotlin/com/lightningkite/reactive/lensing/validation/validation.kt index 6f84f7e..b3a4ccd 100644 --- a/src/commonMain/kotlin/com/lightningkite/reactive/lensing/validation/validation.kt +++ b/src/commonMain/kotlin/com/lightningkite/reactive/lensing/validation/validation.kt @@ -129,9 +129,9 @@ private class ValidationValueLens( } } -fun Validated.checkForIssue(validate: (T) -> Issue?): Validated = ValidatedLens(this, validate) +public fun Validated.checkForIssue(validate: (T) -> Issue?): Validated = ValidatedLens(this, validate) -fun ValidatedValue.checkForIssue(validate: (T) -> Issue?): ValidatedValue = ValidatedValueLens(this, validate) +public fun ValidatedValue.checkForIssue(validate: (T) -> Issue?): ValidatedValue = ValidatedValueLens(this, validate) /** * Adds a validation check to a [MutableValidated] instance. @@ -142,7 +142,7 @@ fun ValidatedValue.checkForIssue(validate: (T) -> Issue?): ValidatedValue * @param validate Function that returns an [Issue] or null for valid values. * @return A [MutableValidated] that tracks issues according to [validate]. */ -fun MutableValidated.checkForIssue(validate: (T) -> Issue?): MutableValidated = MutableValidationLens(this, validate) +public fun MutableValidated.checkForIssue(validate: (T) -> Issue?): MutableValidated = MutableValidationLens(this, validate) /** * Adds a validation check to a [MutableReactive] instance, returning a [MutableValidated] that tracks issues. @@ -152,7 +152,7 @@ fun MutableValidated.checkForIssue(validate: (T) -> Issue?): MutableValid * @param validate Function that returns an [Issue] or null for valid values. * @return A [MutableValidated] that tracks issues according to [validate]. */ -fun MutableReactive.checkForIssue(validate: (T) -> Issue?): MutableValidated = MutableValidationLens(this.validated(), validate) +public fun MutableReactive.checkForIssue(validate: (T) -> Issue?): MutableValidated = MutableValidationLens(this.validated(), validate) /** @@ -164,7 +164,7 @@ fun MutableReactive.checkForIssue(validate: (T) -> Issue?): MutableValida * @param validate Function that returns an [Issue] or null for valid values. * @return A [MutableValidatedValue] that tracks issues according to [validate]. */ -fun MutableValidatedValue.checkForIssue(validate: (T) -> Issue?): MutableValidatedValue = ValidationValueLens(this, validate) +public fun MutableValidatedValue.checkForIssue(validate: (T) -> Issue?): MutableValidatedValue = ValidationValueLens(this, validate) /** * Adds a validation check to a [MutableReactiveValue] instance, returning a [MutableValidatedValue] that tracks issues. @@ -174,4 +174,4 @@ fun MutableValidatedValue.checkForIssue(validate: (T) -> Issue?): Mutable * @param validate Function that returns an [Issue] or null for valid values. * @return A [MutableValidatedValue] that tracks issues according to [validate]. */ -fun MutableReactiveValue.checkForIssue(validate: (T) -> Issue?): MutableValidatedValue = ValidationValueLens(this.validated(), validate) +public fun MutableReactiveValue.checkForIssue(validate: (T) -> Issue?): MutableValidatedValue = ValidationValueLens(this.validated(), validate) diff --git a/src/commonMain/kotlin/com/lightningkite/reactive/lensing/validation/validationLenses.kt b/src/commonMain/kotlin/com/lightningkite/reactive/lensing/validation/validationLenses.kt index 47d37e9..15df7e6 100644 --- a/src/commonMain/kotlin/com/lightningkite/reactive/lensing/validation/validationLenses.kt +++ b/src/commonMain/kotlin/com/lightningkite/reactive/lensing/validation/validationLenses.kt @@ -8,7 +8,7 @@ import com.lightningkite.reactive.lensing.ModifyValueLens import com.lightningkite.reactive.lensing.SetLens import com.lightningkite.reactive.lensing.SetValueLens -class ValidatedSetLens( +public class ValidatedSetLens( source: MutableValidated, get: (T) -> L, set: (L) -> T @@ -24,7 +24,7 @@ class ValidatedSetLens( } } -class ValidatedModifyLens( +public class ValidatedModifyLens( source: MutableValidated, get: (T) -> L, modify: (T, L) -> T @@ -40,7 +40,7 @@ class ValidatedModifyLens( } } -class ValidatedSetValueLens( +public class ValidatedSetValueLens( source: MutableValidatedValue, get: (T) -> L, set: (L) -> T @@ -56,7 +56,7 @@ class ValidatedSetValueLens( } } -class ValidatedModifyValueLens( +public class ValidatedModifyValueLens( source: MutableValidatedValue, get: (T) -> L, modify: (T, L) -> T