@@ -41,7 +41,10 @@ public final class KeyValueDecoder {
4141 /// The strategy to use for decoding `nil`. Defaults to `Optional<Any>.none` which can be decoded to any optional type.
4242 public var nilDecodingStrategy : NilDecodingStrategy = . default
4343
44- /// Initializes `self` with default strategies.
44+ /// The strategy to use for decoding BinaryInteger types. Defaults to `.exact` for lossless conversion between types.
45+ public var intDecodingStrategy : IntDecodingStrategy = . exact
46+
47+ /// Initializes `self` with default strategy.
4548 public init ( ) {
4649 self . userInfo = [ : ]
4750 }
@@ -60,13 +63,21 @@ public final class KeyValueDecoder {
6063 value: value,
6164 codingPath: [ ] ,
6265 userInfo: userInfo,
63- nilDecodingStrategy : nilDecodingStrategy
66+ strategy : strategy
6467 )
6568 return try container. decode ( type)
6669 }
6770
6871 /// Strategy used to decode nil values.
6972 public typealias NilDecodingStrategy = NilCodingStrategy
73+
74+ public enum IntDecodingStrategy {
75+ /// Decodes all number types with lossless conversion or throws error.
76+ case exact
77+
78+ /// Decodes all floating point numbers using the provided rounding rule.
79+ case rounded( rule: FloatingPointRoundingRule )
80+ }
7081}
7182
7283extension KeyValueDecoder {
@@ -80,6 +91,18 @@ extension KeyValueDecoder {
8091
8192private extension KeyValueDecoder {
8293
94+ struct DecodingStrategy {
95+ var optionals : NilDecodingStrategy
96+ var integers : IntDecodingStrategy
97+ }
98+
99+ var strategy : DecodingStrategy {
100+ DecodingStrategy (
101+ optionals: nilDecodingStrategy,
102+ integers: intDecodingStrategy
103+ )
104+ }
105+
83106 struct Decoder : Swift . Decoder {
84107
85108 private let container : SingleContainer
@@ -97,7 +120,7 @@ private extension KeyValueDecoder {
97120 codingPath: codingPath,
98121 storage: container. decode ( [ String : Any ] . self) ,
99122 userInfo: userInfo,
100- nilDecodingStrategy : container. nilDecodingStrategy
123+ strategy : container. strategy
101124 )
102125 return KeyedDecodingContainer ( keyed)
103126 }
@@ -108,7 +131,7 @@ private extension KeyValueDecoder {
108131 codingPath: codingPath,
109132 storage: storage,
110133 userInfo: userInfo,
111- nilDecodingStrategy : container. nilDecodingStrategy
134+ strategy : container. strategy
112135 )
113136 }
114137
@@ -121,28 +144,28 @@ private extension KeyValueDecoder {
121144
122145 let codingPath : [ any CodingKey ]
123146 let userInfo : [ CodingUserInfoKey : Any ]
124- let nilDecodingStrategy : NilDecodingStrategy
147+ let strategy : DecodingStrategy
125148
126149 private var value : Any
127150
128151 init (
129152 value: Any ,
130153 codingPath: [ any CodingKey ] ,
131154 userInfo: [ CodingUserInfoKey : Any ] ,
132- nilDecodingStrategy : NilDecodingStrategy
155+ strategy : DecodingStrategy
133156 ) {
134157 self . value = value
135158 self . codingPath = codingPath
136159 self . userInfo = userInfo
137- self . nilDecodingStrategy = nilDecodingStrategy
160+ self . strategy = strategy
138161 }
139162
140163 func decodeNil( ) -> Bool {
141- nilDecodingStrategy . isNull ( value)
164+ strategy . optionals . isNull ( value)
142165 }
143166
144167 private var valueDescription : String {
145- nilDecodingStrategy . isNull ( value) ? " nil " : String ( describing: type ( of: value) )
168+ strategy . optionals . isNull ( value) ? " nil " : String ( describing: type ( of: value) )
146169 }
147170
148171 func getValue< T> ( of type: T . Type = T . self) throws -> T {
@@ -170,15 +193,13 @@ private extension KeyValueDecoder {
170193 throw DecodingError . typeMismatch ( type, context)
171194 }
172195 return val
173- } else if let double = ( value as? NSNumber ) ? . getDoubleValue ( ) {
174- let val = T ( double)
175- guard Double ( val) == double else {
196+ } else if let double = getDoubleValue ( from: value, using: strategy. integers) {
197+ guard let val = T ( exactly: double) else {
176198 let context = DecodingError . Context ( codingPath: codingPath, debugDescription: " \( valueDescription) at \( codingPath. makeKeyPath ( ) ) , cannot be exactly represented by \( type) " )
177199 throw DecodingError . typeMismatch ( type, context)
178200 }
179201 return val
180- }
181- else {
202+ } else {
182203 let context = DecodingError . Context ( codingPath: codingPath, debugDescription: " Expected BinaryInteger at \( codingPath. makeKeyPath ( ) ) , found \( valueDescription) " )
183204 if decodeNil ( ) {
184205 throw DecodingError . valueNotFound ( type, context)
@@ -188,6 +209,18 @@ private extension KeyValueDecoder {
188209 }
189210 }
190211
212+ func getDoubleValue( from value: Any , using strategy: IntDecodingStrategy ) -> Double ? {
213+ guard let double = ( value as? NSNumber ) ? . getDoubleValue ( ) else {
214+ return nil
215+ }
216+ switch strategy {
217+ case . exact:
218+ return double
219+ case . rounded( rule: let rule) :
220+ return double. rounded ( rule)
221+ }
222+ }
223+
191224 func decode( _ type: Bool . Type ) throws -> Bool {
192225 try getValue ( )
193226 }
@@ -322,18 +355,18 @@ private extension KeyValueDecoder {
322355 let storage : [ String : Any ]
323356 let codingPath : [ any CodingKey ]
324357 private let userInfo : [ CodingUserInfoKey : Any ]
325- private let nilDecodingStrategy : NilDecodingStrategy
358+ private let strategy : DecodingStrategy
326359
327360 init (
328361 codingPath: [ any CodingKey ] ,
329362 storage: [ String : Any ] ,
330363 userInfo: [ CodingUserInfoKey : Any ] ,
331- nilDecodingStrategy : NilDecodingStrategy
364+ strategy : DecodingStrategy
332365 ) {
333366 self . codingPath = codingPath
334367 self . storage = storage
335368 self . userInfo = userInfo
336- self . nilDecodingStrategy = nilDecodingStrategy
369+ self . strategy = strategy
337370 }
338371
339372 var allKeys : [ Key ] {
@@ -357,7 +390,7 @@ private extension KeyValueDecoder {
357390 value: value,
358391 codingPath: path,
359392 userInfo: userInfo,
360- nilDecodingStrategy : nilDecodingStrategy
393+ strategy : strategy
361394 )
362395 }
363396
@@ -435,7 +468,7 @@ private extension KeyValueDecoder {
435468 codingPath: container. codingPath,
436469 storage: container. decode ( [ String : Any ] . self) ,
437470 userInfo: userInfo,
438- nilDecodingStrategy : nilDecodingStrategy
471+ strategy : strategy
439472 )
440473 return KeyedDecodingContainer < NestedKey > ( keyed)
441474 }
@@ -446,7 +479,7 @@ private extension KeyValueDecoder {
446479 codingPath: container. codingPath,
447480 storage: container. decode ( [ Any ] . self) ,
448481 userInfo: userInfo,
449- nilDecodingStrategy : nilDecodingStrategy
482+ strategy : strategy
450483 )
451484 }
452485
@@ -455,7 +488,7 @@ private extension KeyValueDecoder {
455488 value: storage,
456489 codingPath: codingPath,
457490 userInfo: userInfo,
458- nilDecodingStrategy : nilDecodingStrategy
491+ strategy : strategy
459492 )
460493 return Decoder ( container: container)
461494 }
@@ -471,18 +504,18 @@ private extension KeyValueDecoder {
471504
472505 let storage : [ Any ]
473506 private let userInfo : [ CodingUserInfoKey : Any ]
474- private let nilDecodingStrategy : NilDecodingStrategy
507+ private let strategy : DecodingStrategy
475508
476509 init (
477510 codingPath: [ any CodingKey ] ,
478511 storage: [ Any ] ,
479512 userInfo: [ CodingUserInfoKey : Any ] ,
480- nilDecodingStrategy : NilDecodingStrategy
513+ strategy : DecodingStrategy
481514 ) {
482515 self . codingPath = codingPath
483516 self . storage = storage
484517 self . userInfo = userInfo
485- self . nilDecodingStrategy = nilDecodingStrategy
518+ self . strategy = strategy
486519 self . currentIndex = storage. startIndex
487520 }
488521
@@ -507,7 +540,7 @@ private extension KeyValueDecoder {
507540 value: storage [ currentIndex] ,
508541 codingPath: path,
509542 userInfo: userInfo,
510- nilDecodingStrategy : nilDecodingStrategy
543+ strategy : strategy
511544 )
512545 }
513546
@@ -600,7 +633,7 @@ private extension KeyValueDecoder {
600633 value: storage,
601634 codingPath: codingPath,
602635 userInfo: userInfo,
603- nilDecodingStrategy : nilDecodingStrategy
636+ strategy : strategy
604637 )
605638 return Decoder ( container: container)
606639 }
0 commit comments