Skip to content

Commit

Permalink
LceState.flatMap
Browse files Browse the repository at this point in the history
Fixes: #47
  • Loading branch information
Nikolay Kochetkov committed May 8, 2023
1 parent 0028c76 commit 32025c0
Show file tree
Hide file tree
Showing 2 changed files with 246 additions and 0 deletions.
68 changes: 68 additions & 0 deletions lce/src/commonMain/kotlin/com/motorro/rxlcemodel/lce/LceState.kt
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,74 @@ inline fun <DATA_1: Any, DATA_2: Any, DATA_3: Any> LceState<DATA_1>.combine(othe
}
}

/**
* Flat-maps Lce states with the result of other
* Here is the result state matrix
* | Receiver | mapper | Result |
* |------------|------------|------------|
* | Loading | Loading | Loading |
* | Loading | Content | Loading |
* | Loading | Error | Error |
* | Loading | Terminated | Terminated |
* | Content | Loading | Loading |
* | Content | Content | Content* |
* | Content | Error | Error |
* | Content | Terminated | Terminated |
* | Error | Loading | Error |
* | Error | Content | Error |
* | Error | Error | Error |
* | Error | Terminated | Terminated |
* | Terminated | Loading | Terminated |
* | Terminated | Content | Terminated |
* | Terminated | Error | Terminated |
* | Terminated | Terminated | Terminated |
* @receiver An Lce state that has a priority in final state resolution
* @param mapper Returns a new [LceState] as a product of receiver data
*/
inline fun <DATA_1: Any, DATA_2: Any> LceState<DATA_1>.flatMap(mapper: (data1: DATA_1) -> LceState<DATA_2>): LceState<DATA_2> = when (this) {
is LceState.Loading -> {
val other = data?.let(mapper)
val data3 = other?.data
val dataIsValid = dataIsValid && true == other?.dataIsValid

when (other) {
is LceState.Error -> LceState.Error(data3, dataIsValid, other.error)
is LceState.Terminated -> LceState.Terminated
else -> LceState.Loading(data3, dataIsValid, this.type)
}
}
is LceState.Content -> {
val other = mapper(data)
val data3 = other.data
val dataIsValid = dataIsValid && other.dataIsValid

when (other) {
is LceState.Error -> LceState.Error(data3, dataIsValid, other.error)
is LceState.Terminated -> LceState.Terminated
is LceState.Loading -> LceState.Loading(data3, dataIsValid, other.type)
is LceState.Content -> if (null == data3) {
LceState.Loading(null, false)
} else {
LceState.Content(data3, dataIsValid)
}
}
}
is LceState.Error -> {
val other = data?.let(mapper)
val data3 = other?.data
val dataIsValid = dataIsValid && true == other?.dataIsValid

if (other is LceState.Terminated) {
LceState.Terminated
} else {
LceState.Error(data3, dataIsValid, error)
}
}
is LceState.Terminated -> {
LceState.Terminated
}
}

/**
* Runs transformation [block] catching any error and wrapping it to [LceState.Error]:
* - The output data will be null
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
/*
* Copyright 2022 Nikolai Kotchetkov.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.motorro.rxlcemodel.lce

import com.motorro.rxlcemodel.lce.LceState.Loading.Type
import kotlin.test.*

class LceStateFlatMapKtTest {
@Test
fun loadingAndLoadingResultsInLoading() {
val state1 = LceState.Loading(1, false)
val state2 = LceState.Loading(2, false, Type.REFRESHING)
val state3 = state1.flatMap { _ -> state2 }
assertTrue(state3 is LceState.Loading)
assertFalse(state3.dataIsValid)
assertEquals(Type.LOADING, state3.type)
}

@Test
fun loadingAndContentResultsInLoading() {
val state1 = LceState.Loading(1, false)
val state2 = LceState.Content(2, true)
val state3 = state1.flatMap { _ -> state2 }
assertTrue(state3 is LceState.Loading)
assertFalse(state3.dataIsValid)
assertEquals(Type.LOADING, state3.type)
}

@Test
fun loadingAndErrorResultsInError() {
val error = Error("Some error")
val state1 = LceState.Loading(1, false)
val state2 = LceState.Error(2, false, error)
val state3 = state1.flatMap { _ -> state2 }
assertTrue(state3 is LceState.Error)
assertFalse(state3.dataIsValid)
assertEquals(error, state3.error)
}

@Test
fun loadingAndTerminatedResultsInTerminated() {
val state1 = LceState.Loading(1, false)
val state2 = LceState.Terminated
val state3 = state1.flatMap { _ -> state2 }
assertTrue(state3 is LceState.Terminated)
}

@Test
fun contentAndLoadingResultsInLoading() {
val state1 = LceState.Content(1, true)
val state2 = LceState.Loading(3, false, Type.REFRESHING)
val state3 = state1.flatMap { _ -> state2 }
assertTrue(state3 is LceState.Loading)
assertFalse(state3.dataIsValid)
assertEquals(3, state3.data)
assertEquals(Type.REFRESHING, state3.type)
}

@Test
fun contentAndContentResultsInContent() {
val state1 = LceState.Content(1, true)
val state2 = LceState.Content(3, true)
val state3 = state1.flatMap { _ -> state2 }
assertTrue(state3 is LceState.Content)
assertTrue(state3.dataIsValid)
assertEquals(3, state3.data)
}

@Test
fun contentAndErrorResultsInError() {
val error = Error("Some error")
val state1 = LceState.Content(1, true)
val state2 = LceState.Error(3, false, error)
val state3 = state1.flatMap { _ -> state2 }
assertTrue(state3 is LceState.Error)
assertFalse(state3.dataIsValid)
assertEquals(3, state3.data)
assertEquals(error, state3.error)
}

@Test
fun contentAndTerminatedResultsInTerminated() {
val state1 = LceState.Content(1, true)
val state2 = LceState.Terminated
val state3 = state1.flatMap { _ -> state2 }
assertTrue(state3 is LceState.Terminated)
}

@Test
fun errorAndLoadingResultsInError() {
val error = Error("Some error")
val state1 = LceState.Error(1, false, error)
val state2 = LceState.Loading(3, false, Type.REFRESHING)
val state3 = state1.flatMap { _ -> state2 }
assertTrue(state3 is LceState.Error)
assertFalse(state3.dataIsValid)
assertEquals(3, state3.data)
assertEquals(error, state3.error)
}

@Test
fun errorAndContentResultsInError() {
val error = Error("Some error")
val state1 = LceState.Error(1, false, error)
val state2 = LceState.Content(3, true)
val state3 = state1.flatMap { _ -> state2 }
assertTrue(state3 is LceState.Error)
assertFalse(state3.dataIsValid)
assertEquals(3, state3.data)
assertEquals(error, state3.error)
}

@Test
fun errorAndErrorResultsInReceiverError() {
val error1 = Error("Some error 1")
val error2 = Error("Some error 2")
val state1 = LceState.Error(1, false, error1)
val state2 = LceState.Error(3, true, error2)
val state3 = state1.flatMap { _ -> state2 }
assertTrue(state3 is LceState.Error)
assertFalse(state3.dataIsValid)
assertEquals(3, state3.data)
assertEquals(error1, state3.error)
}

@Test
fun errorAndTerminatedResultsInTerminated() {
val error = Error("Some error")
val state1 = LceState.Error(1, false, error)
val state2 = LceState.Terminated
val state3 = state1.flatMap { _ -> state2 }
assertTrue(state3 is LceState.Terminated)
}

@Test
fun terminatedAndLoadingResultsInTerminated() {
val state1 = LceState.Terminated
val state2 = LceState.Loading(2, false, Type.REFRESHING)
val state3 = state1.flatMap { _ -> state2 }
assertTrue(state3 is LceState.Terminated)
}

@Test
fun terminatedAndContentResultsInTerminated() {
val state1 = LceState.Terminated
val state2 = LceState.Content(2, true)
val state3 = state1.flatMap { _ -> state2 }
assertTrue(state3 is LceState.Terminated)
}

@Test
fun terminatedAndErrorResultsInReceiverTerminated() {
val error = Error("Some error")
val state1 = LceState.Terminated
val state2 = LceState.Error(2, true, error)
val state3 = state1.flatMap { _ -> state2 }
assertTrue(state3 is LceState.Terminated)
}

@Test
fun terminatedAndTerminatedResultsInTerminated() {
val state1 = LceState.Terminated
val state2 = LceState.Terminated
val state3 = state1.flatMap { _ -> state2 }
assertTrue(state3 is LceState.Terminated)
}
}

0 comments on commit 32025c0

Please sign in to comment.