@@ -10,20 +10,35 @@ 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    )
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/* *
0 commit comments