Skip to content

Commit

Permalink
2.5.0-alpha12 (#65)
Browse files Browse the repository at this point in the history
  • Loading branch information
Nek-12 committed May 1, 2024
2 parents f359d12 + 971e3e0 commit ee5ecbe
Show file tree
Hide file tree
Showing 67 changed files with 758 additions and 428 deletions.
3 changes: 0 additions & 3 deletions .github/workflows/assign_self.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,3 @@ jobs:
- uses: samspills/assign-pr-to-author@v1.0.2
with:
repo-token: '${{ secrets.GITHUB_TOKEN }}'
- uses: actions/labeler@v4.0.3
with:
sync-labels: true
4 changes: 2 additions & 2 deletions buildSrc/src/main/kotlin/Config.kt
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,11 @@ object Config {

const val artifactId = "$group.$artifact"

const val versionCode = 4
const val versionCode = 5
const val majorRelease = 2
const val minorRelease = 5
const val patch = 0
const val postfix = "-alpha11" // include dash (-)
const val postfix = "-alpha12" // include dash (-)
const val majorVersionName = "$majorRelease.$minorRelease.$patch"
const val versionName = "$majorVersionName$postfix"
const val url = "https://github.com/respawn-app/FlowMVI"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package pro.respawn.flowmvi.store
package pro.respawn.flowmvi

import kotlinx.atomicfu.atomic
import kotlinx.coroutines.CoroutineScope
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,5 +23,5 @@ public interface StateProvider<out S : MVIState> {
* Accessing and modifying the state this way will **circumvent ALL plugins** and will not make state updates atomic.
*/
@DelicateStoreApi
public val state: S get() = states.value
public val state: S
}
Original file line number Diff line number Diff line change
Expand Up @@ -54,4 +54,12 @@ public interface StateReceiver<S : MVIState> {
*/
@FlowMVIDSL
public fun useState(block: S.() -> S)

/**
* Obtain the current value of state in an unsafe manner.
* It is recommended to always use [withState] or [updateState] always as obtaining this value can lead
* to data races when the state transaction changes the value of the state previously obtained.
*/
@DelicateStoreApi
public val state: S
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ package pro.respawn.flowmvi.api
* or an exception while trying to recover from another exception (which is prohibited).
* You may also use this to bypass store plugins handling this particular exception.
*/
public class UnrecoverableException(
public open class UnrecoverableException(
override val cause: Exception? = null,
override val message: String? = null,
) : IllegalStateException(message, cause)
Original file line number Diff line number Diff line change
Expand Up @@ -19,17 +19,19 @@ import pro.respawn.flowmvi.api.Store
public inline fun <S : MVIState, I : MVIIntent, A : MVIAction> ImmutableContainer<S, I, A>.lazyStore(
initial: S,
scope: CoroutineScope,
mode: LazyThreadSafetyMode = LazyThreadSafetyMode.SYNCHRONIZED,
@BuilderInference crossinline configure: BuildStore<S, I, A>
): Lazy<Store<S, I, A>> = pro.respawn.flowmvi.dsl.lazyStore(initial, scope, configure)
): Lazy<Store<S, I, A>> = pro.respawn.flowmvi.dsl.lazyStore(initial, scope, mode, configure)

/**
* Alias for [pro.respawn.flowmvi.dsl.lazyStore]
*/
@FlowMVIDSL
public inline fun <S : MVIState, I : MVIIntent, A : MVIAction> ImmutableContainer<S, I, A>.lazyStore(
initial: S,
mode: LazyThreadSafetyMode = LazyThreadSafetyMode.SYNCHRONIZED,
@BuilderInference crossinline configure: BuildStore<S, I, A>
): Lazy<Store<S, I, A>> = pro.respawn.flowmvi.dsl.lazyStore(initial, configure)
): Lazy<Store<S, I, A>> = pro.respawn.flowmvi.dsl.lazyStore(initial = initial, mode = mode, configure = configure)

/**
* Alias for [pro.respawn.flowmvi.dsl.store]
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
@file:Suppress("NOTHING_TO_INLINE")
@file:Suppress("NOTHING_TO_INLINE", "KotlinRedundantDiagnosticSuppress")

package pro.respawn.flowmvi.dsl

Expand All @@ -9,6 +9,7 @@ import pro.respawn.flowmvi.api.MVIAction
import pro.respawn.flowmvi.api.MVIIntent

// ----- intents -----

/**
* Alias for [IntentReceiver.intent] for multiple intents
*
Expand Down Expand Up @@ -42,6 +43,7 @@ public suspend inline fun <I : MVIIntent> IntentReceiver<I>.emit(
): Unit = intents.forEach { emit(it) }

// ----- actions -----

/**
* Alias for [ActionReceiver.action] for multiple actions
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import pro.respawn.flowmvi.api.StorePlugin
* when the store is created.
*/
public class LazyPluginBuilder<S : MVIState, I : MVIIntent, A : MVIAction> @PublishedApi internal constructor(
public val config: StoreConfiguration<S>
public val config: StoreConfiguration<S>,
) : StorePluginBuilder<S, I, A>()

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package pro.respawn.flowmvi.dsl

import kotlinx.coroutines.channels.BufferOverflow
import pro.respawn.flowmvi.StoreImpl
import pro.respawn.flowmvi.api.ActionShareBehavior
import pro.respawn.flowmvi.api.FlowMVIDSL
import pro.respawn.flowmvi.api.LazyPlugin
Expand All @@ -14,7 +15,6 @@ import pro.respawn.flowmvi.logging.NoOpStoreLogger
import pro.respawn.flowmvi.logging.PlatformStoreLogger
import pro.respawn.flowmvi.logging.StoreLogger
import pro.respawn.flowmvi.plugins.compositePlugin
import pro.respawn.flowmvi.store.StoreImpl
import kotlin.coroutines.CoroutineContext

public typealias BuildStore<S, I, A> = StoreBuilder<S, I, A>.() -> Unit
Expand Down Expand Up @@ -136,7 +136,7 @@ public class StoreBuilder<S : MVIState, I : MVIIntent, A : MVIAction> @Published
public fun install(
plugin: LazyPlugin<S, I, A>,
vararg other: LazyPlugin<S, I, A>,
): Unit = install(other.asSequence().plus(plugin).asIterable())
): Unit = install(sequenceOf(plugin).plus(other).asIterable())

/**
* Create and install a new [StorePlugin].
Expand All @@ -157,13 +157,14 @@ public class StoreBuilder<S : MVIState, I : MVIIntent, A : MVIAction> @Published
/**
* Alias for [install]
*/
@FlowMVIDSL
public fun LazyPlugin<S, I, A>.install(): Unit = install(this)

// it's important to first convert the collection to an immutable before iterating, or the
// iterator will throw
@PublishedApi
@FlowMVIDSL
internal operator fun invoke(): Store<S, I, A> = config(initial).let { config ->
// it's important to first convert the collection to an immutable before iterating, or the
// iterator will throw
StoreImpl(config, compositePlugin(plugins.toSet().map { it(config) }))
}
}
11 changes: 5 additions & 6 deletions core/src/commonMain/kotlin/pro/respawn/flowmvi/dsl/StoreDsl.kt
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,7 @@ import kotlin.jvm.JvmName
public inline fun <S : MVIState, I : MVIIntent, A : MVIAction> store(
initial: S,
@BuilderInference configure: BuildStore<S, I, A>,
): Store<S, I, A> = StoreBuilder<S, I, A>(initial).run {
configure()
invoke()
}
): Store<S, I, A> = StoreBuilder<S, I, A>(initial).apply(configure).invoke()

/**
* Build a new [Store] using [StoreBuilder].
Expand Down Expand Up @@ -68,8 +65,9 @@ public inline fun <S : MVIState, I : MVIIntent> store(
@FlowMVIDSL
public inline fun <S : MVIState, I : MVIIntent, A : MVIAction> lazyStore(
initial: S,
mode: LazyThreadSafetyMode = LazyThreadSafetyMode.SYNCHRONIZED,
@BuilderInference crossinline configure: BuildStore<S, I, A>,
): Lazy<Store<S, I, A>> = lazy { store(initial, configure) }
): Lazy<Store<S, I, A>> = lazy(mode) { store(initial, configure) }

/**
* Build a new [Store] using [StoreBuilder].
Expand All @@ -79,5 +77,6 @@ public inline fun <S : MVIState, I : MVIIntent, A : MVIAction> lazyStore(
public inline fun <S : MVIState, I : MVIIntent, A : MVIAction> lazyStore(
initial: S,
scope: CoroutineScope,
mode: LazyThreadSafetyMode = LazyThreadSafetyMode.SYNCHRONIZED,
@BuilderInference crossinline configure: BuildStore<S, I, A>,
): Lazy<Store<S, I, A>> = lazy { store(initial, configure).apply { start(scope) } }
): Lazy<Store<S, I, A>> = lazy(mode) { store(initial, configure).apply { start(scope) } }
Original file line number Diff line number Diff line change
Expand Up @@ -6,22 +6,26 @@ import pro.respawn.flowmvi.api.MVIIntent
import pro.respawn.flowmvi.api.MVIState
import pro.respawn.flowmvi.api.PipelineContext
import pro.respawn.flowmvi.api.StorePlugin
import pro.respawn.flowmvi.util.setOnce

/**
* A class that builds a new [StorePlugin]
* For more documentation, see [StorePlugin]
*
* Builder methods will throw [IllegalArgumentException] if they are assigned multiple times. Each plugin can only
* have **one** block per each type of [StorePlugin] callback.
*/

@FlowMVIDSL
public open class StorePluginBuilder<S : MVIState, I : MVIIntent, A : MVIAction> @PublishedApi internal constructor() {

private var intent: suspend PipelineContext<S, I, A>.(I) -> I? = { it }
private var state: suspend PipelineContext<S, I, A>.(old: S, new: S) -> S? = { _, new -> new }
private var action: suspend PipelineContext<S, I, A>.(A) -> A? = { it }
private var exception: suspend PipelineContext<S, I, A>.(e: Exception) -> Exception? = { it }
private var start: suspend PipelineContext<S, I, A>.() -> Unit = { }
private var subscribe: suspend PipelineContext<S, I, A>.(subscriberCount: Int) -> Unit = {}
private var unsubscribe: suspend PipelineContext<S, I, A>.(subscriberCount: Int) -> Unit = {}
private var stop: (e: Exception?) -> Unit = { }
private var intent: (suspend PipelineContext<S, I, A>.(I) -> I?)? = null
private var state: (suspend PipelineContext<S, I, A>.(old: S, new: S) -> S?)? = null
private var action: (suspend PipelineContext<S, I, A>.(A) -> A?)? = null
private var exception: (suspend PipelineContext<S, I, A>.(e: Exception) -> Exception?)? = null
private var start: (suspend PipelineContext<S, I, A>.() -> Unit)? = null
private var subscribe: (suspend PipelineContext<S, I, A>.(subscriberCount: Int) -> Unit)? = null
private var unsubscribe: (suspend PipelineContext<S, I, A>.(subscriberCount: Int) -> Unit)? = null
private var stop: ((e: Exception?) -> Unit)? = null

/**
* @see [StorePlugin.name]
Expand All @@ -33,84 +37,100 @@ public open class StorePluginBuilder<S : MVIState, I : MVIIntent, A : MVIAction>
* @see [StorePlugin.onIntent]
*/
@FlowMVIDSL
public fun onIntent(block: suspend PipelineContext<S, I, A>.(intent: I) -> I?) {
intent = block
}
public fun onIntent(block: suspend PipelineContext<S, I, A>.(intent: I) -> I?): Unit = setOnce(::intent, block)

/**
* @see [StorePlugin.onState]
*/
@FlowMVIDSL
public fun onState(block: suspend PipelineContext<S, I, A>.(old: S, new: S) -> S?) {
state = block
}
public fun onState(block: suspend PipelineContext<S, I, A>.(old: S, new: S) -> S?): Unit = setOnce(::state, block)

/**
* @see [StorePlugin.onStart]
*/
@FlowMVIDSL
public fun onStart(block: suspend PipelineContext<S, I, A>.() -> Unit) {
start = block
}
public fun onStart(block: suspend PipelineContext<S, I, A>.() -> Unit): Unit = setOnce(::start, block)

/**
* @see [StorePlugin.onStop]
*/
@FlowMVIDSL
public fun onStop(block: (e: Exception?) -> Unit) {
stop = block
}
public fun onStop(block: (e: Exception?) -> Unit): Unit = setOnce(::stop, block)

/**
* @see [StorePlugin.onException]
*/
@FlowMVIDSL
public fun onException(block: suspend PipelineContext<S, I, A>.(e: Exception) -> Exception?) {
exception = block
}
public fun onException(
block: suspend PipelineContext<S, I, A>.(e: Exception) -> Exception?
): Unit = setOnce(::exception, block)

/**
* @see [StorePlugin.onAction]
*/
@FlowMVIDSL
public fun onAction(block: suspend PipelineContext<S, I, A>.(action: A) -> A?) {
action = block
}
public fun onAction(block: suspend PipelineContext<S, I, A>.(action: A) -> A?): Unit = setOnce(::action, block)

/**
* @see [StorePlugin.onSubscribe]
*/
@FlowMVIDSL
public fun onSubscribe(
block: suspend PipelineContext<S, I, A>.(subscriberCount: Int) -> Unit
) {
subscribe = block
}
): Unit = setOnce(::subscribe, block)

/**
* @see StorePlugin.onUnsubscribe
*/
@FlowMVIDSL
public fun onUnsubscribe(
block: suspend PipelineContext<S, I, A>.(subscriberCount: Int) -> Unit
) {
unsubscribe = block
}
): Unit = setOnce(::unsubscribe, block)

@FlowMVIDSL
@PublishedApi
internal fun build(): StorePlugin<S, I, A> = object : StorePlugin<S, I, A> {
override val name = this@StorePluginBuilder.name
override suspend fun PipelineContext<S, I, A>.onStart() = start()
override suspend fun PipelineContext<S, I, A>.onState(old: S, new: S): S? = state(old, new)
override suspend fun PipelineContext<S, I, A>.onIntent(intent: I): I? = intent(this, intent)
override suspend fun PipelineContext<S, I, A>.onAction(action: A): A? = action(this, action)
override suspend fun PipelineContext<S, I, A>.onException(e: Exception): Exception? = exception(e)
override suspend fun PipelineContext<S, I, A>.onSubscribe(subscriberCount: Int) = subscribe(subscriberCount)
override suspend fun PipelineContext<S, I, A>.onUnsubscribe(subscriberCount: Int) = unsubscribe(subscriberCount)
override fun onStop(e: Exception?): Unit = stop(e)
override fun toString(): String = "StorePlugin \"${name ?: super.toString()}\""
override suspend fun PipelineContext<S, I, A>.onStart() {
this@StorePluginBuilder.start?.invoke(this)
}

override suspend fun PipelineContext<S, I, A>.onState(old: S, new: S): S? {
val block = this@StorePluginBuilder.state ?: return new
return block(old, new)
}

override suspend fun PipelineContext<S, I, A>.onIntent(intent: I): I? {
val block = this@StorePluginBuilder.intent ?: return intent
return block(intent)
}

override suspend fun PipelineContext<S, I, A>.onAction(action: A): A? {
val block = this@StorePluginBuilder.action ?: return action
return block(action)
}

override suspend fun PipelineContext<S, I, A>.onException(e: Exception): Exception? {
val block = this@StorePluginBuilder.exception ?: return e
return block(e)
}

override suspend fun PipelineContext<S, I, A>.onSubscribe(subscriberCount: Int) {
this@StorePluginBuilder.subscribe?.invoke(this, subscriberCount)
}

override suspend fun PipelineContext<S, I, A>.onUnsubscribe(subscriberCount: Int) {
this@StorePluginBuilder.unsubscribe?.invoke(this, subscriberCount)
}

override fun onStop(e: Exception?) {
stop?.invoke(e)
}

override fun toString(): String = name?.let { "StorePlugin \"$it\"" } ?: super.toString()

override fun hashCode(): Int = name?.hashCode() ?: super.hashCode()

override fun equals(other: Any?): Boolean = when {
other !is StorePlugin<*, *, *> -> false
other.name == null && name == null -> this === other
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
package pro.respawn.flowmvi.logging

/**
* A [StoreLogger] that does nothing.
*/
public val NoOpStoreLogger: StoreLogger by lazy { StoreLogger { _, _, _ -> } }

/**
* A logger that prints to console ([println])
*/
Expand Down

This file was deleted.

Loading

0 comments on commit ee5ecbe

Please sign in to comment.