@@ -207,10 +207,15 @@ extension FileTranslator {
207
207
func translateStructBlueprintAnyOfDecoder( properties: [ ( property: PropertyBlueprint , isKeyValuePair: Bool ) ] )
208
208
-> Declaration
209
209
{
210
- let assignExprs : [ Expression ] = properties. map { ( property, isKeyValuePair) in
210
+ let errorArrayDecl : Declaration = . createErrorArrayDecl( )
211
+ let assignBlocks : [ CodeBlock ] = properties. map { ( property, isKeyValuePair) in
211
212
let decoderExpr : Expression =
212
213
isKeyValuePair ? . initFromDecoderExpr( ) : . decodeFromSingleValueContainerExpr( )
213
- return . assignment( left: . identifierPattern( property. swiftSafeName) , right: . optionalTry( decoderExpr) )
214
+ let assignExpr : Expression = . assignment(
215
+ left: . identifierPattern( property. swiftSafeName) ,
216
+ right: . try ( decoderExpr)
217
+ )
218
+ return . expression( assignExpr. wrapInDoCatchAppendArrayExpr ( ) )
214
219
}
215
220
let atLeastOneNotNilCheckExpr : Expression = . try (
216
221
. identifierType( TypeName . decodingError) . dot ( " verifyAtLeastOneSchemaIsNotNil " )
@@ -220,9 +225,12 @@ extension FileTranslator {
220
225
expression: . literal( . array( properties. map { . identifierPattern( $0. property. swiftSafeName) } ) )
221
226
) , . init( label: " type " , expression: . identifierPattern( " Self " ) . dot ( " self " ) ) ,
222
227
. init( label: " codingPath " , expression: . identifierPattern( " decoder " ) . dot ( " codingPath " ) ) ,
228
+ . init( label: " errors " , expression: . identifierPattern( " errors " ) ) ,
223
229
] )
224
230
)
225
- return decoderInitializer ( body: assignExprs. map { . expression( $0) } + [ . expression( atLeastOneNotNilCheckExpr) ] )
231
+ return decoderInitializer (
232
+ body: [ . declaration( errorArrayDecl) ] + assignBlocks + [ . expression( atLeastOneNotNilCheckExpr) ]
233
+ )
226
234
}
227
235
228
236
/// Returns a declaration of an anyOf encoder implementation.
@@ -254,37 +262,51 @@ extension FileTranslator {
254
262
/// - Parameter cases: The names of the cases to be decoded.
255
263
/// - Returns: A `Declaration` representing the `OneOf` decoder implementation.
256
264
func translateOneOfWithoutDiscriminatorDecoder( cases: [ ( name: String , isKeyValuePair: Bool ) ] ) -> Declaration {
265
+ let errorArrayDecl : Declaration = . createErrorArrayDecl( )
257
266
let assignExprs : [ Expression ] = cases. map { ( caseName, isKeyValuePair) in
258
267
let decoderExpr : Expression =
259
268
isKeyValuePair ? . initFromDecoderExpr( ) : . decodeFromSingleValueContainerExpr( )
260
- return . doStatement(
261
- . init(
262
- doStatement: [
263
- . expression(
264
- . assignment(
265
- left: . identifierPattern( " self " ) ,
266
- right: . dot( caseName) . call ( [ . init( label: nil , expression: . try ( decoderExpr) ) ] )
267
- )
268
- ) , . expression( . return( ) ) ,
269
- ] ,
270
- catchBody: [ ]
271
- )
272
- )
269
+ let body : [ CodeBlock ] = [
270
+ . expression(
271
+ . assignment(
272
+ left: . identifierPattern( " self " ) ,
273
+ right: . dot( caseName) . call ( [ . init( label: nil , expression: . try ( decoderExpr) ) ] )
274
+ )
275
+ ) , . expression( . return( ) ) ,
276
+ ]
277
+ return body. wrapInDoCatchAppendArrayExpr ( )
273
278
}
274
-
275
- let otherExprs : [ CodeBlock ] = [ . expression( translateOneOfDecoderThrowOnUnknownExpr ( ) ) ]
276
- return decoderInitializer ( body: ( assignExprs) . map { . expression( $0) } + otherExprs)
279
+ let otherExprs : [ CodeBlock ] = [ . expression( translateOneOfDecoderThrowOnNoCaseDecodedExpr ( ) ) ]
280
+ return decoderInitializer (
281
+ body: [ . declaration( errorArrayDecl) ] + ( assignExprs) . map { . expression( $0) } + otherExprs
282
+ )
277
283
}
278
284
285
+ /// Returns an expression that throws an error when a oneOf discriminator
286
+ /// failed to match any known cases.
287
+ func translateOneOfDecoderThrowOnUnknownExpr( discriminatorSwiftName: String ) -> Expression {
288
+ . unaryKeyword(
289
+ kind: . throw,
290
+ expression: . identifierType( TypeName . decodingError) . dot ( " unknownOneOfDiscriminator " )
291
+ . call ( [
292
+ . init(
293
+ label: " discriminatorKey " ,
294
+ expression: . identifierPattern( Constants . Codable. codingKeysName) . dot ( discriminatorSwiftName)
295
+ ) , . init( label: " discriminatorValue " , expression: . identifierPattern( " discriminator " ) ) ,
296
+ . init( label: " codingPath " , expression: . identifierPattern( " decoder " ) . dot ( " codingPath " ) ) ,
297
+ ] )
298
+ )
299
+ }
279
300
/// Returns an expression that throws an error when a oneOf failed
280
301
/// to match any documented cases.
281
- func translateOneOfDecoderThrowOnUnknownExpr ( ) -> Expression {
302
+ func translateOneOfDecoderThrowOnNoCaseDecodedExpr ( ) -> Expression {
282
303
. unaryKeyword(
283
304
kind: . throw,
284
305
expression: . identifierType( TypeName . decodingError) . dot ( " failedToDecodeOneOfSchema " )
285
306
. call ( [
286
307
. init( label: " type " , expression: . identifierPattern( " Self " ) . dot ( " self " ) ) ,
287
308
. init( label: " codingPath " , expression: . identifierPattern( " decoder " ) . dot ( " codingPath " ) ) ,
309
+ . init( label: " errors " , expression: . identifierPattern( " errors " ) ) ,
288
310
] )
289
311
)
290
312
}
@@ -311,7 +333,9 @@ extension FileTranslator {
311
333
]
312
334
)
313
335
}
314
- let otherExprs : [ CodeBlock ] = [ . expression( translateOneOfDecoderThrowOnUnknownExpr ( ) ) ]
336
+ let otherExprs : [ CodeBlock ] = [
337
+ . expression( translateOneOfDecoderThrowOnUnknownExpr ( discriminatorSwiftName: discriminatorName) )
338
+ ]
315
339
let body : [ CodeBlock ] = [
316
340
. declaration( . decoderContainerOfKeysVar( ) ) ,
317
341
. declaration(
@@ -408,6 +432,31 @@ fileprivate extension Expression {
408
432
static func decodeFromSingleValueContainerExpr( ) -> Expression {
409
433
. identifierPattern( " decoder " ) . dot ( " decodeFromSingleValueContainer " ) . call ( [ ] )
410
434
}
435
+ /// Returns a new expression that wraps the provided expression in
436
+ /// a do/catch block where the error is appended to an array.
437
+ ///
438
+ /// Assumes the existence of an "errors" variable in the current scope.
439
+ /// - Returns: The expression.
440
+ func wrapInDoCatchAppendArrayExpr( ) -> Expression { [ CodeBlock . expression ( self ) ] . wrapInDoCatchAppendArrayExpr ( ) }
441
+ }
442
+
443
+ fileprivate extension Array where Element == CodeBlock {
444
+ /// Returns a new expression that wraps the provided code blocks in
445
+ /// a do/catch block where the error is appended to an array.
446
+ ///
447
+ /// Assumes the existence of an "errors" variable in the current scope.
448
+ /// - Returns: The expression.
449
+ func wrapInDoCatchAppendArrayExpr( ) -> Expression {
450
+ . do(
451
+ self ,
452
+ catchBody: [
453
+ . expression(
454
+ . identifierPattern( " errors " ) . dot ( " append " )
455
+ . call ( [ . init( label: nil , expression: . identifierPattern( " error " ) ) ] )
456
+ )
457
+ ]
458
+ )
459
+ }
411
460
}
412
461
413
462
fileprivate extension Declaration {
@@ -424,6 +473,11 @@ fileprivate extension Declaration {
424
473
)
425
474
)
426
475
}
476
+ /// Creates a new declaration that creates a local array of errors.
477
+ /// - Returns: The declaration.
478
+ static func createErrorArrayDecl( ) -> Declaration {
479
+ . variable( kind: . var, left: " errors " , type: . array( . any( . member( " Error " ) ) ) , right: . literal( . array( [ ] ) ) )
480
+ }
427
481
}
428
482
429
483
fileprivate extension FileTranslator {
0 commit comments