Skip to content

Commit

Permalink
Merge pull request #50 from icerockdev/develop
Browse files Browse the repository at this point in the history
Release 0.7.0
  • Loading branch information
Alex009 authored Apr 16, 2023
2 parents f1d29f9 + 7b43153 commit af0c9f1
Show file tree
Hide file tree
Showing 18 changed files with 168 additions and 90 deletions.
9 changes: 9 additions & 0 deletions errors/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
* Copyright 2020 IceRock MAG Inc. Use of this source code is governed by the Apache 2.0 license.
*/

import dev.icerock.gradle.tasks.GenerateMultiplatformResourcesTask

plugins {
id("dev.icerock.moko.gradle.multiplatform.mobile")
id("kotlin-parcelize")
Expand All @@ -14,6 +16,10 @@ plugins {
group = "dev.icerock.moko"
version = libs.versions.mokoErrorsVersion.get()

android {
namespace = "dev.icerock.moko.errors"
}

dependencies {
commonMainImplementation(libs.coroutines)

Expand All @@ -28,3 +34,6 @@ multiplatformResources {
multiplatformResourcesPackage = "dev.icerock.moko.errors"
}

tasks.withType<GenerateMultiplatformResourcesTask>().configureEach {
tasks.getByName("sourcesJar").dependsOn(this)
}
2 changes: 0 additions & 2 deletions errors/src/androidMain/AndroidManifest.xml

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ class AlertDialogFragment : DialogFragment() {
.setMessage(settings.messageText)
.setPositiveButton(settings.positiveButtonText) { _, _ -> }
.create()
} ?: throw IllegalStateException("Activity can't be null.")
} ?: error("Activity can't be null.")
}

@Parcelize
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,13 +39,14 @@ internal class ExceptionHandlerContextImpl<T : Any, R>(
override suspend fun execute(): HandlerResult<R, Throwable> {
return try {
HandlerResult.Success(block())
} catch (e: Exception) {
} catch (e: CancellationException) {
// Don't handle coroutines CancellationException
if (e is CancellationException) throw e
throw e
} catch (e: Exception) {
onCatch?.invoke(e)
val isHandled = isHandledByCustomCatcher(e)
val isHandled: Boolean = isHandledByCustomCatcher(e)
if (!isHandled) { // If not handled by a custom catcher
val errorValue = exceptionMapper(e)
val errorValue: T = exceptionMapper(e)
eventsDispatcher.dispatchEvent {
showError(e, errorValue)
}
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -10,20 +10,35 @@ import dev.icerock.moko.resources.desc.desc
import kotlin.native.concurrent.ThreadLocal
import kotlin.reflect.KClass

internal typealias ThrowableMapper = (Throwable) -> Any

@Suppress("TooManyFunctions")
@ThreadLocal
object ExceptionMappersStorage {

private val fallbackValuesMap: MutableMap<KClass<out Any>, Any> = mutableMapOf(
StringDesc::class to MR.strings.moko_errors_unknownError.desc()
private val containers: MutableMap<KClass<*>, MappersContainer<*>> = mutableMapOf(
StringDesc::class to MappersContainer<StringDesc>(
mappers = emptyList(),
fallback = { MR.strings.moko_errors_unknownError.desc() }
)
)
private val notifiers: MutableList<(Throwable, KClass<*>, Any) -> Unit> = mutableListOf()

private fun <T : Any> getOrCreateContainer(resultClass: KClass<T>): MappersContainer<T> {
val existContainer: MappersContainer<*>? = containers[resultClass]
if (existContainer != null) return existContainer as MappersContainer<T>

private val mappersMap: MutableMap<KClass<out Any>, MutableMap<KClass<out Throwable>, ThrowableMapper>> =
mutableMapOf()
private val conditionMappers: MutableMap<KClass<out Any>, MutableList<ConditionPair>> =
mutableMapOf()
return MappersContainer<T>(
mappers = emptyList(),
fallback = { throw FallbackValueNotFoundException(resultClass) }
).also { containers[resultClass] = it }
}

private fun <T : Any> updateContainer(
resultClass: KClass<T>,
block: (MappersContainer<T>) -> MappersContainer<T>
) {
val container: MappersContainer<T> = getOrCreateContainer(resultClass)
containers[resultClass] = block(container)
}

/**
* Register simple mapper (E) -> T.
Expand All @@ -33,11 +48,16 @@ object ExceptionMappersStorage {
exceptionClass: KClass<E>,
mapper: (E) -> T
): ExceptionMappersStorage {
if (!mappersMap.containsKey(resultClass)) {
mappersMap[resultClass] = mutableMapOf()
updateContainer(
resultClass
) { container ->
container.copy(
mappers = container.mappers + ThrowableMapperItem(
mapper = { mapper(it as E) },
isApplied = { it::class == exceptionClass }
)
)
}
@Suppress("UNCHECKED_CAST")
mappersMap[resultClass]?.put(exceptionClass, mapper as ThrowableMapper)
return this
}

Expand All @@ -46,12 +66,19 @@ object ExceptionMappersStorage {
*/
fun <T : Any> register(
resultClass: KClass<T>,
conditionPair: ConditionPair
isApplied: (Throwable) -> Boolean,
mapper: (Throwable) -> T
): ExceptionMappersStorage {
if (!conditionMappers.containsKey(resultClass)) {
conditionMappers[resultClass] = mutableListOf()
updateContainer(
resultClass
) { container ->
container.copy(
mappers = container.mappers + ThrowableMapperItem(
mapper = mapper,
isApplied = isApplied
)
)
}
conditionMappers[resultClass]?.add(conditionPair)
return this
}

Expand All @@ -76,10 +103,8 @@ object ExceptionMappersStorage {
noinline mapper: (Throwable) -> T
): ExceptionMappersStorage = register(
resultClass = T::class,
conditionPair = ConditionPair(
condition,
mapper as ThrowableMapper
)
isApplied = condition,
mapper = mapper
)

/**
Expand All @@ -91,19 +116,27 @@ object ExceptionMappersStorage {
*/
fun <E : Throwable, T : Any> find(
resultClass: KClass<T>,
throwable: E,
exceptionClass: KClass<out E>
throwable: E
): ((E) -> T)? {
@Suppress("UNCHECKED_CAST")
val mapper = conditionMappers[resultClass]
?.find { it.condition(throwable) }
?.mapper as? ((E) -> T)
?: mappersMap[resultClass]?.get(exceptionClass) as? ((E) -> T)
val container: MappersContainer<T>? = containers[resultClass] as MappersContainer<T>?

return if (mapper == null && throwable !is Exception) {
if (container == null && throwable !is Exception) {
throw throwable
} else {
mapper
} else if (container == null) {
return null
}

val mapper: (Throwable) -> T = container.mappers
.firstOrNull { it.isApplied(throwable) }
?.mapper
?: container.fallback

return { exception ->
val result: T = mapper(exception)
notifiers.forEach { notifier ->
notifier(exception, resultClass, result)
}
result
}
}

Expand All @@ -116,16 +149,21 @@ object ExceptionMappersStorage {
*/
inline fun <E : Throwable, reified T : Any> find(throwable: E): ((E) -> T)? = find(
resultClass = T::class,
throwable = throwable,
exceptionClass = throwable::class
throwable = throwable
)

/**
* Sets fallback (default) value for [T] errors type.
*/
fun <T : Any> setFallbackValue(clazz: KClass<T>, value: T): ExceptionMappersStorage {
fallbackValuesMap[clazz] = value
return ExceptionMappersStorage
updateContainer(
clazz
) { container ->
container.copy(
fallback = { value }
)
}
return this
}

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

/**
* Returns fallback (default) value for [T] errors type.
* If there is no default value for the class [T], then [FallbackValueNotFoundException]
* exception will be thrown.
* Sets fallback (default) factory for [T] errors type.
*/
fun <T : Any> getFallbackValue(clazz: KClass<T>): T {
@Suppress("UNCHECKED_CAST")
return fallbackValuesMap[clazz] as? T
?: throw FallbackValueNotFoundException(clazz)
fun <T : Any> setFallbackFactory(
clazz: KClass<T>,
factory: (Throwable) -> T
): ExceptionMappersStorage {
updateContainer(
clazz
) { container ->
container.copy(
fallback = factory
)
}
return this
}

/**
* Returns fallback (default) value for [T] errors type.
* If there is no default value for the class [T], then [FallbackValueNotFoundException]
* exception will be thrown.
*/
inline fun <reified T : Any> getFallbackValue(): T = getFallbackValue(T::class)
inline fun <reified T : Any> setFallbackFactory(
noinline factory: (Throwable) -> T
): ExceptionMappersStorage = setFallbackFactory(T::class, factory)

/**
* Factory method that creates mappers (Throwable) -> T with a registered fallback value for
* class [T].
*/
fun <E : Throwable, T : Any> throwableMapper(clazz: KClass<T>): (e: E) -> T {
val fallback = getFallbackValue(clazz)
return { e ->
find(clazz, e, e::class)?.invoke(e) ?: fallback
find(clazz, e)?.invoke(e) ?: throw FallbackValueNotFoundException(clazz)
}
}

inline fun <E : Throwable, reified T : Any> throwableMapper(): (e: E) -> T {
return dev.icerock.moko.errors.mappers.throwableMapper()
}

/**
* Listen all mappers calls. Useful for logging
*
* @param block - lambda that will be called when exception map to some class
*/
fun onEach(
block: (Throwable, KClass<*>, Any) -> Unit
): ExceptionMappersStorage {
notifiers.add(block)
return this
}
}

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
/*
* Copyright 2023 IceRock MAG Inc. Use of this source code is governed by the Apache 2.0 license.
*/

package dev.icerock.moko.errors.mappers

internal data class MappersContainer<T>(
val mappers: List<ThrowableMapperItem<T>>,
val fallback: ThrowableMapper<T>
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
/*
* Copyright 2023 IceRock MAG Inc. Use of this source code is governed by the Apache 2.0 license.
*/

package dev.icerock.moko.errors.mappers

internal typealias ThrowableMapper<T> = (Throwable) -> T

internal data class ThrowableMapperItem<T>(
val mapper: ThrowableMapper<T>,
val isApplied: (Throwable) -> Boolean
)
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@

package dev.icerock.moko.errors.presenters

import dev.icerock.moko.errors.MR
import dev.icerock.moko.resources.desc.StringDesc
import dev.icerock.moko.resources.desc.desc
import dev.icerock.moko.errors.MR

/**
* In iOS there is no such thing as snackbar, so it shows [AlertErrorPresenter].
Expand Down
8 changes: 3 additions & 5 deletions gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,12 @@ org.gradle.configureondemand=false
org.gradle.parallel=true

kotlin.code.style=official
kotlin.native.enableDependencyPropagation=false
kotlin.mpp.enableGranularSourceSetsMetadata=true
kotlin.mpp.enableCompatibilityMetadataVariant=true
kotlin.mpp.androidSourceSetLayoutVersion=2

android.useAndroidX=true

moko.android.targetSdk=30
moko.android.compileSdk=30
moko.android.targetSdk=33
moko.android.compileSdk=33
moko.android.minSdk=16

moko.publish.name=MOKO errors
Expand Down
22 changes: 11 additions & 11 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
[versions]
kotlinVersion = "1.6.10"
androidAppCompatVersion = "1.2.0"
materialDesignVersion = "1.4.0"
androidLifecycleVersion = "2.1.0"
androidCoreTestingVersion = "2.1.0"
coroutinesVersion = "1.6.0-native-mt"
mokoMvvmVersion = "0.12.0"
mokoResourcesVersion = "0.18.0"
mokoErrorsVersion = "0.6.0"
kotlinVersion = "1.8.10"
androidAppCompatVersion = "1.6.1"
materialDesignVersion = "1.8.0"
androidLifecycleVersion = "2.2.0"
androidCoreTestingVersion = "2.2.0"
coroutinesVersion = "1.6.4"
mokoMvvmVersion = "0.16.0"
mokoResourcesVersion = "0.21.2"
mokoErrorsVersion = "0.7.0"

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

# gradle plugins
kotlinGradlePlugin = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", version.ref = "kotlinVersion" }
androidGradlePlugin = { module = "com.android.tools.build:gradle", version = "7.0.4" }
androidGradlePlugin = { module = "com.android.tools.build:gradle", version = "7.4.2" }
mokoResourcesGradlePlugin = { module = "dev.icerock.moko:resources-generator", version.ref = "mokoResourcesVersion" }
mokoGradlePlugin = { module = "dev.icerock.moko:moko-gradle-plugin", version = "0.1.0" }
mokoGradlePlugin = { module = "dev.icerock.moko:moko-gradle-plugin", version = "0.3.0" }
2 changes: 1 addition & 1 deletion gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.1-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
Loading

0 comments on commit af0c9f1

Please sign in to comment.