@@ -10,20 +10,34 @@ import dev.icerock.moko.resources.desc.desc
1010import kotlin.native.concurrent.ThreadLocal
1111import kotlin.reflect.KClass
1212
13- internal typealias ThrowableMapper = (Throwable ) -> Any
14-
1513@Suppress(" TooManyFunctions" )
1614@ThreadLocal
1715object 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 )
2223
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 ()
24+ private fun <T : Any > getOrCreateContainer (resultClass : KClass <T >): MappersContainer <T > {
25+ val existContainer: MappersContainer <* >? = containers[resultClass]
26+ if (existContainer != null ) return existContainer as MappersContainer <T >
27+
28+ return MappersContainer <T >(
29+ mappers = emptyList(),
30+ fallback = { throw FallbackValueNotFoundException (resultClass) }
31+ ).also { containers[resultClass] = it }
32+ }
33+
34+ private fun <T : Any > updateContainer (
35+ resultClass : KClass <T >,
36+ block : (MappersContainer <T >) -> MappersContainer <T >
37+ ) {
38+ val container: MappersContainer <T > = getOrCreateContainer(resultClass)
39+ containers[resultClass] = block(container)
40+ }
2741
2842 /* *
2943 * Register simple mapper (E) -> T.
@@ -33,11 +47,16 @@ object ExceptionMappersStorage {
3347 exceptionClass : KClass <E >,
3448 mapper : (E ) -> T
3549 ): ExceptionMappersStorage {
36- if (! mappersMap.containsKey(resultClass)) {
37- mappersMap[resultClass] = mutableMapOf ()
50+ updateContainer(
51+ resultClass
52+ ) { container ->
53+ container.copy(
54+ mappers = container.mappers + ThrowableMapperItem (
55+ mapper = { mapper(it as E ) },
56+ isApplied = { it::class == exceptionClass }
57+ )
58+ )
3859 }
39- @Suppress(" UNCHECKED_CAST" )
40- mappersMap[resultClass]?.put(exceptionClass, mapper as ThrowableMapper )
4160 return this
4261 }
4362
@@ -46,12 +65,19 @@ object ExceptionMappersStorage {
4665 */
4766 fun <T : Any > register (
4867 resultClass : KClass <T >,
49- conditionPair : ConditionPair
68+ isApplied : (Throwable ) -> Boolean ,
69+ mapper : (Throwable ) -> T
5070 ): ExceptionMappersStorage {
51- if (! conditionMappers.containsKey(resultClass)) {
52- conditionMappers[resultClass] = mutableListOf ()
71+ updateContainer(
72+ resultClass
73+ ) { container ->
74+ container.copy(
75+ mappers = container.mappers + ThrowableMapperItem (
76+ mapper = mapper,
77+ isApplied = isApplied
78+ )
79+ )
5380 }
54- conditionMappers[resultClass]?.add(conditionPair)
5581 return this
5682 }
5783
@@ -76,10 +102,8 @@ object ExceptionMappersStorage {
76102 noinline mapper : (Throwable ) -> T
77103 ): ExceptionMappersStorage = register(
78104 resultClass = T ::class ,
79- conditionPair = ConditionPair (
80- condition,
81- mapper as ThrowableMapper
82- )
105+ isApplied = condition,
106+ mapper = mapper
83107 )
84108
85109 /* *
@@ -91,20 +115,20 @@ object ExceptionMappersStorage {
91115 */
92116 fun <E : Throwable , T : Any > find (
93117 resultClass : KClass <T >,
94- throwable : E ,
95- exceptionClass : KClass <out E >
118+ throwable : E
96119 ): ((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 )
120+ val container: MappersContainer <T >? = containers[resultClass] as MappersContainer <T >?
102121
103- return if (mapper == null && throwable !is Exception ) {
122+ if (container == null && throwable !is Exception ) {
104123 throw throwable
105- } else {
106- mapper
124+ } else if (container == null ) {
125+ return null
107126 }
127+
128+ return container.mappers
129+ .firstOrNull { it.isApplied(throwable) }
130+ ?.mapper
131+ ? : container.fallback
108132 }
109133
110134 /* *
@@ -116,16 +140,21 @@ object ExceptionMappersStorage {
116140 */
117141 inline fun <E : Throwable , reified T : Any > find (throwable : E ): ((E ) -> T )? = find(
118142 resultClass = T ::class ,
119- throwable = throwable,
120- exceptionClass = throwable::class
143+ throwable = throwable
121144 )
122145
123146 /* *
124147 * Sets fallback (default) value for [T] errors type.
125148 */
126149 fun <T : Any > setFallbackValue (clazz : KClass <T >, value : T ): ExceptionMappersStorage {
127- fallbackValuesMap[clazz] = value
128- return ExceptionMappersStorage
150+ updateContainer(
151+ clazz
152+ ) { container ->
153+ container.copy(
154+ fallback = { value }
155+ )
156+ }
157+ return this
129158 }
130159
131160 /* *
@@ -135,37 +164,35 @@ object ExceptionMappersStorage {
135164 setFallbackValue(T ::class , value)
136165
137166 /* *
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.
167+ * Sets fallback (default) factory for [T] errors type.
141168 */
142- fun <T : Any > getFallbackValue (clazz : KClass <T >): T {
143- @Suppress(" UNCHECKED_CAST" )
144- return fallbackValuesMap[clazz] as ? T
145- ? : throw FallbackValueNotFoundException (clazz)
169+ fun <T : Any > setFallbackFactory (
170+ clazz : KClass <T >,
171+ factory : (Throwable ) -> T
172+ ): ExceptionMappersStorage {
173+ updateContainer(
174+ clazz
175+ ) { container ->
176+ container.copy(
177+ fallback = factory
178+ )
179+ }
180+ return this
146181 }
147182
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 )
183+ inline fun <reified T : Any > setFallbackFactory (
184+ noinline factory : (Throwable ) -> T
185+ ): ExceptionMappersStorage = setFallbackFactory(T ::class , factory)
154186
155187 /* *
156188 * Factory method that creates mappers (Throwable) -> T with a registered fallback value for
157189 * class [T].
158190 */
159191 fun <E : Throwable , T : Any > throwableMapper (clazz : KClass <T >): (e: E ) -> T {
160- val fallback = getFallbackValue(clazz)
161192 return { e ->
162- find(clazz, e, e:: class )?.invoke(e) ? : fallback
193+ find(clazz, e)?.invoke(e) ? : throw FallbackValueNotFoundException (clazz)
163194 }
164195 }
165-
166- inline fun <E : Throwable , reified T : Any > throwableMapper (): (e: E ) -> T {
167- return dev.icerock.moko.errors.mappers.throwableMapper()
168- }
169196}
170197
171198/* *
0 commit comments