Skip to content

Commit af0c9f1

Browse files
authored
Merge pull request #50 from icerockdev/develop
Release 0.7.0
2 parents f1d29f9 + 7b43153 commit af0c9f1

File tree

18 files changed

+168
-90
lines changed

18 files changed

+168
-90
lines changed

errors/build.gradle.kts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
* Copyright 2020 IceRock MAG Inc. Use of this source code is governed by the Apache 2.0 license.
33
*/
44

5+
import dev.icerock.gradle.tasks.GenerateMultiplatformResourcesTask
6+
57
plugins {
68
id("dev.icerock.moko.gradle.multiplatform.mobile")
79
id("kotlin-parcelize")
@@ -14,6 +16,10 @@ plugins {
1416
group = "dev.icerock.moko"
1517
version = libs.versions.mokoErrorsVersion.get()
1618

19+
android {
20+
namespace = "dev.icerock.moko.errors"
21+
}
22+
1723
dependencies {
1824
commonMainImplementation(libs.coroutines)
1925

@@ -28,3 +34,6 @@ multiplatformResources {
2834
multiplatformResourcesPackage = "dev.icerock.moko.errors"
2935
}
3036

37+
tasks.withType<GenerateMultiplatformResourcesTask>().configureEach {
38+
tasks.getByName("sourcesJar").dependsOn(this)
39+
}

errors/src/androidMain/AndroidManifest.xml

Lines changed: 0 additions & 2 deletions
This file was deleted.

errors/src/androidMain/kotlin/dev/icerock/moko/errors/presenters/AlertDialogFragment.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ class AlertDialogFragment : DialogFragment() {
2828
.setMessage(settings.messageText)
2929
.setPositiveButton(settings.positiveButtonText) { _, _ -> }
3030
.create()
31-
} ?: throw IllegalStateException("Activity can't be null.")
31+
} ?: error("Activity can't be null.")
3232
}
3333

3434
@Parcelize

errors/src/commonMain/kotlin/dev/icerock/moko/errors/handler/ExceptionHandlerContextImpl.kt

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -39,13 +39,14 @@ internal class ExceptionHandlerContextImpl<T : Any, R>(
3939
override suspend fun execute(): HandlerResult<R, Throwable> {
4040
return try {
4141
HandlerResult.Success(block())
42-
} catch (e: Exception) {
42+
} catch (e: CancellationException) {
4343
// Don't handle coroutines CancellationException
44-
if (e is CancellationException) throw e
44+
throw e
45+
} catch (e: Exception) {
4546
onCatch?.invoke(e)
46-
val isHandled = isHandledByCustomCatcher(e)
47+
val isHandled: Boolean = isHandledByCustomCatcher(e)
4748
if (!isHandled) { // If not handled by a custom catcher
48-
val errorValue = exceptionMapper(e)
49+
val errorValue: T = exceptionMapper(e)
4950
eventsDispatcher.dispatchEvent {
5051
showError(e, errorValue)
5152
}

errors/src/commonMain/kotlin/dev/icerock/moko/errors/mappers/ConditionPair.kt

Lines changed: 0 additions & 10 deletions
This file was deleted.

errors/src/commonMain/kotlin/dev/icerock/moko/errors/mappers/ExceptionMappersStorage.kt

Lines changed: 101 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -10,20 +10,35 @@ import dev.icerock.moko.resources.desc.desc
1010
import kotlin.native.concurrent.ThreadLocal
1111
import kotlin.reflect.KClass
1212

13-
internal typealias ThrowableMapper = (Throwable) -> Any
14-
1513
@Suppress("TooManyFunctions")
1614
@ThreadLocal
1715
object ExceptionMappersStorage {
1816

19-
private val fallbackValuesMap: MutableMap<KClass<out Any>, Any> = mutableMapOf(
20-
StringDesc::class to MR.strings.moko_errors_unknownError.desc()
17+
private val containers: MutableMap<KClass<*>, MappersContainer<*>> = mutableMapOf(
18+
StringDesc::class to MappersContainer<StringDesc>(
19+
mappers = emptyList(),
20+
fallback = { MR.strings.moko_errors_unknownError.desc() }
21+
)
2122
)
23+
private val notifiers: MutableList<(Throwable, KClass<*>, Any) -> Unit> = mutableListOf()
24+
25+
private fun <T : Any> getOrCreateContainer(resultClass: KClass<T>): MappersContainer<T> {
26+
val existContainer: MappersContainer<*>? = containers[resultClass]
27+
if (existContainer != null) return existContainer as MappersContainer<T>
2228

23-
private val mappersMap: MutableMap<KClass<out Any>, MutableMap<KClass<out Throwable>, ThrowableMapper>> =
24-
mutableMapOf()
25-
private val conditionMappers: MutableMap<KClass<out Any>, MutableList<ConditionPair>> =
26-
mutableMapOf()
29+
return MappersContainer<T>(
30+
mappers = emptyList(),
31+
fallback = { throw FallbackValueNotFoundException(resultClass) }
32+
).also { containers[resultClass] = it }
33+
}
34+
35+
private fun <T : Any> updateContainer(
36+
resultClass: KClass<T>,
37+
block: (MappersContainer<T>) -> MappersContainer<T>
38+
) {
39+
val container: MappersContainer<T> = getOrCreateContainer(resultClass)
40+
containers[resultClass] = block(container)
41+
}
2742

2843
/**
2944
* Register simple mapper (E) -> T.
@@ -33,11 +48,16 @@ object ExceptionMappersStorage {
3348
exceptionClass: KClass<E>,
3449
mapper: (E) -> T
3550
): ExceptionMappersStorage {
36-
if (!mappersMap.containsKey(resultClass)) {
37-
mappersMap[resultClass] = mutableMapOf()
51+
updateContainer(
52+
resultClass
53+
) { container ->
54+
container.copy(
55+
mappers = container.mappers + ThrowableMapperItem(
56+
mapper = { mapper(it as E) },
57+
isApplied = { it::class == exceptionClass }
58+
)
59+
)
3860
}
39-
@Suppress("UNCHECKED_CAST")
40-
mappersMap[resultClass]?.put(exceptionClass, mapper as ThrowableMapper)
4161
return this
4262
}
4363

@@ -46,12 +66,19 @@ object ExceptionMappersStorage {
4666
*/
4767
fun <T : Any> register(
4868
resultClass: KClass<T>,
49-
conditionPair: ConditionPair
69+
isApplied: (Throwable) -> Boolean,
70+
mapper: (Throwable) -> T
5071
): ExceptionMappersStorage {
51-
if (!conditionMappers.containsKey(resultClass)) {
52-
conditionMappers[resultClass] = mutableListOf()
72+
updateContainer(
73+
resultClass
74+
) { container ->
75+
container.copy(
76+
mappers = container.mappers + ThrowableMapperItem(
77+
mapper = mapper,
78+
isApplied = isApplied
79+
)
80+
)
5381
}
54-
conditionMappers[resultClass]?.add(conditionPair)
5582
return this
5683
}
5784

@@ -76,10 +103,8 @@ object ExceptionMappersStorage {
76103
noinline mapper: (Throwable) -> T
77104
): ExceptionMappersStorage = register(
78105
resultClass = T::class,
79-
conditionPair = ConditionPair(
80-
condition,
81-
mapper as ThrowableMapper
82-
)
106+
isApplied = condition,
107+
mapper = mapper
83108
)
84109

85110
/**
@@ -91,19 +116,27 @@ object ExceptionMappersStorage {
91116
*/
92117
fun <E : Throwable, T : Any> find(
93118
resultClass: KClass<T>,
94-
throwable: E,
95-
exceptionClass: KClass<out E>
119+
throwable: E
96120
): ((E) -> T)? {
97-
@Suppress("UNCHECKED_CAST")
98-
val mapper = conditionMappers[resultClass]
99-
?.find { it.condition(throwable) }
100-
?.mapper as? ((E) -> T)
101-
?: mappersMap[resultClass]?.get(exceptionClass) as? ((E) -> T)
121+
val container: MappersContainer<T>? = containers[resultClass] as MappersContainer<T>?
102122

103-
return if (mapper == null && throwable !is Exception) {
123+
if (container == null && throwable !is Exception) {
104124
throw throwable
105-
} else {
106-
mapper
125+
} else if (container == null) {
126+
return null
127+
}
128+
129+
val mapper: (Throwable) -> T = container.mappers
130+
.firstOrNull { it.isApplied(throwable) }
131+
?.mapper
132+
?: container.fallback
133+
134+
return { exception ->
135+
val result: T = mapper(exception)
136+
notifiers.forEach { notifier ->
137+
notifier(exception, resultClass, result)
138+
}
139+
result
107140
}
108141
}
109142

@@ -116,16 +149,21 @@ object ExceptionMappersStorage {
116149
*/
117150
inline fun <E : Throwable, reified T : Any> find(throwable: E): ((E) -> T)? = find(
118151
resultClass = T::class,
119-
throwable = throwable,
120-
exceptionClass = throwable::class
152+
throwable = throwable
121153
)
122154

123155
/**
124156
* Sets fallback (default) value for [T] errors type.
125157
*/
126158
fun <T : Any> setFallbackValue(clazz: KClass<T>, value: T): ExceptionMappersStorage {
127-
fallbackValuesMap[clazz] = value
128-
return ExceptionMappersStorage
159+
updateContainer(
160+
clazz
161+
) { container ->
162+
container.copy(
163+
fallback = { value }
164+
)
165+
}
166+
return this
129167
}
130168

131169
/**
@@ -135,37 +173,51 @@ object ExceptionMappersStorage {
135173
setFallbackValue(T::class, value)
136174

137175
/**
138-
* Returns fallback (default) value for [T] errors type.
139-
* If there is no default value for the class [T], then [FallbackValueNotFoundException]
140-
* exception will be thrown.
176+
* Sets fallback (default) factory for [T] errors type.
141177
*/
142-
fun <T : Any> getFallbackValue(clazz: KClass<T>): T {
143-
@Suppress("UNCHECKED_CAST")
144-
return fallbackValuesMap[clazz] as? T
145-
?: throw FallbackValueNotFoundException(clazz)
178+
fun <T : Any> setFallbackFactory(
179+
clazz: KClass<T>,
180+
factory: (Throwable) -> T
181+
): ExceptionMappersStorage {
182+
updateContainer(
183+
clazz
184+
) { container ->
185+
container.copy(
186+
fallback = factory
187+
)
188+
}
189+
return this
146190
}
147191

148-
/**
149-
* Returns fallback (default) value for [T] errors type.
150-
* If there is no default value for the class [T], then [FallbackValueNotFoundException]
151-
* exception will be thrown.
152-
*/
153-
inline fun <reified T : Any> getFallbackValue(): T = getFallbackValue(T::class)
192+
inline fun <reified T : Any> setFallbackFactory(
193+
noinline factory: (Throwable) -> T
194+
): ExceptionMappersStorage = setFallbackFactory(T::class, factory)
154195

155196
/**
156197
* Factory method that creates mappers (Throwable) -> T with a registered fallback value for
157198
* class [T].
158199
*/
159200
fun <E : Throwable, T : Any> throwableMapper(clazz: KClass<T>): (e: E) -> T {
160-
val fallback = getFallbackValue(clazz)
161201
return { e ->
162-
find(clazz, e, e::class)?.invoke(e) ?: fallback
202+
find(clazz, e)?.invoke(e) ?: throw FallbackValueNotFoundException(clazz)
163203
}
164204
}
165205

166206
inline fun <E : Throwable, reified T : Any> throwableMapper(): (e: E) -> T {
167207
return dev.icerock.moko.errors.mappers.throwableMapper()
168208
}
209+
210+
/**
211+
* Listen all mappers calls. Useful for logging
212+
*
213+
* @param block - lambda that will be called when exception map to some class
214+
*/
215+
fun onEach(
216+
block: (Throwable, KClass<*>, Any) -> Unit
217+
): ExceptionMappersStorage {
218+
notifiers.add(block)
219+
return this
220+
}
169221
}
170222

171223
/**
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
/*
2+
* Copyright 2023 IceRock MAG Inc. Use of this source code is governed by the Apache 2.0 license.
3+
*/
4+
5+
package dev.icerock.moko.errors.mappers
6+
7+
internal data class MappersContainer<T>(
8+
val mappers: List<ThrowableMapperItem<T>>,
9+
val fallback: ThrowableMapper<T>
10+
)
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
/*
2+
* Copyright 2023 IceRock MAG Inc. Use of this source code is governed by the Apache 2.0 license.
3+
*/
4+
5+
package dev.icerock.moko.errors.mappers
6+
7+
internal typealias ThrowableMapper<T> = (Throwable) -> T
8+
9+
internal data class ThrowableMapperItem<T>(
10+
val mapper: ThrowableMapper<T>,
11+
val isApplied: (Throwable) -> Boolean
12+
)

errors/src/iosMain/kotlin/dev/icerock/moko/errors/presenters/SnackBarErrorPresenter.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@
44

55
package dev.icerock.moko.errors.presenters
66

7+
import dev.icerock.moko.errors.MR
78
import dev.icerock.moko.resources.desc.StringDesc
89
import dev.icerock.moko.resources.desc.desc
9-
import dev.icerock.moko.errors.MR
1010

1111
/**
1212
* In iOS there is no such thing as snackbar, so it shows [AlertErrorPresenter].

gradle.properties

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,12 @@ org.gradle.configureondemand=false
33
org.gradle.parallel=true
44

55
kotlin.code.style=official
6-
kotlin.native.enableDependencyPropagation=false
7-
kotlin.mpp.enableGranularSourceSetsMetadata=true
8-
kotlin.mpp.enableCompatibilityMetadataVariant=true
6+
kotlin.mpp.androidSourceSetLayoutVersion=2
97

108
android.useAndroidX=true
119

12-
moko.android.targetSdk=30
13-
moko.android.compileSdk=30
10+
moko.android.targetSdk=33
11+
moko.android.compileSdk=33
1412
moko.android.minSdk=16
1513

1614
moko.publish.name=MOKO errors

gradle/libs.versions.toml

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
[versions]
2-
kotlinVersion = "1.6.10"
3-
androidAppCompatVersion = "1.2.0"
4-
materialDesignVersion = "1.4.0"
5-
androidLifecycleVersion = "2.1.0"
6-
androidCoreTestingVersion = "2.1.0"
7-
coroutinesVersion = "1.6.0-native-mt"
8-
mokoMvvmVersion = "0.12.0"
9-
mokoResourcesVersion = "0.18.0"
10-
mokoErrorsVersion = "0.6.0"
2+
kotlinVersion = "1.8.10"
3+
androidAppCompatVersion = "1.6.1"
4+
materialDesignVersion = "1.8.0"
5+
androidLifecycleVersion = "2.2.0"
6+
androidCoreTestingVersion = "2.2.0"
7+
coroutinesVersion = "1.6.4"
8+
mokoMvvmVersion = "0.16.0"
9+
mokoResourcesVersion = "0.21.2"
10+
mokoErrorsVersion = "0.7.0"
1111

1212
[libraries]
1313
# android
@@ -30,6 +30,6 @@ androidCoreTesting = { module = "androidx.arch.core:core-testing", version.ref =
3030

3131
# gradle plugins
3232
kotlinGradlePlugin = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", version.ref = "kotlinVersion" }
33-
androidGradlePlugin = { module = "com.android.tools.build:gradle", version = "7.0.4" }
33+
androidGradlePlugin = { module = "com.android.tools.build:gradle", version = "7.4.2" }
3434
mokoResourcesGradlePlugin = { module = "dev.icerock.moko:resources-generator", version.ref = "mokoResourcesVersion" }
35-
mokoGradlePlugin = { module = "dev.icerock.moko:moko-gradle-plugin", version = "0.1.0" }
35+
mokoGradlePlugin = { module = "dev.icerock.moko:moko-gradle-plugin", version = "0.3.0" }
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
distributionBase=GRADLE_USER_HOME
22
distributionPath=wrapper/dists
3-
distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-bin.zip
3+
distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.1-bin.zip
44
zipStoreBase=GRADLE_USER_HOME
55
zipStorePath=wrapper/dists

0 commit comments

Comments
 (0)