@@ -76,14 +76,15 @@ extension Includes: Decodable where I: Decodable {
7676 }
7777 }
7878 guard errors. count == error. individualTypeFailures. count else {
79- throw IncludesDecodingError ( error: error, idx: idx)
79+ throw IncludesDecodingError ( error: error, idx: idx, totalIncludesCount : container . count ?? 0 )
8080 }
8181 throw IncludesDecodingError (
8282 error: IncludeDecodingError ( failures: errors) ,
83- idx: idx
83+ idx: idx,
84+ totalIncludesCount: container. count ?? 0
8485 )
8586 } catch let error {
86- throw IncludesDecodingError ( error: error, idx: idx)
87+ throw IncludesDecodingError ( error: error, idx: idx, totalIncludesCount : container . count ?? 0 )
8788 }
8889 }
8990
@@ -208,7 +209,13 @@ extension Includes where I: _Poly11 {
208209// MARK: - DecodingError
209210public struct IncludesDecodingError : Swift . Error , Equatable {
210211 public let error : Swift . Error
212+ /// The zero-based index of the include that failed to decode.
211213 public let idx : Int
214+ /// The total count of includes in the document that failed to decode.
215+ ///
216+ /// In other words, "of `totalIncludesCount` includes, the `(idx + 1)`th
217+ /// include failed to decode.
218+ public let totalIncludesCount : Int
212219
213220 public static func == ( lhs: Self , rhs: Self ) -> Bool {
214221 return lhs. idx == rhs. idx
@@ -218,18 +225,55 @@ public struct IncludesDecodingError: Swift.Error, Equatable {
218225
219226extension IncludesDecodingError : CustomStringConvertible {
220227 public var description : String {
221- return " Include \( idx + 1 ) failed to parse: \( error) "
228+ let ordinalSuffix : String
229+ if ( idx % 100 ) + 1 > 9 && ( idx % 100 ) + 1 < 20 {
230+ // the teens
231+ ordinalSuffix = " th "
232+ } else {
233+ switch ( ( idx % 10 ) + 1 ) {
234+ case 1 :
235+ ordinalSuffix = " st "
236+ case 2 :
237+ ordinalSuffix = " nd "
238+ case 3 :
239+ ordinalSuffix = " rd "
240+ default :
241+ ordinalSuffix = " th "
242+ }
243+ }
244+ let ordinalDescription = " \( idx + 1 ) \( ordinalSuffix) "
245+
246+ return " Out of the \( totalIncludesCount) includes in the document, the \( ordinalDescription) one failed to parse: \( error) "
222247 }
223248}
224249
225250public struct IncludeDecodingError : Swift . Error , Equatable , CustomStringConvertible {
226251 public let failures : [ ResourceObjectDecodingError ]
227252
228253 public var description : String {
254+ // concise error when all failures are mismatched JSON:API types:
255+ if case let . jsonTypeMismatch( foundType: foundType) ? = failures. first? . cause,
256+ failures. allSatisfy ( { $0. cause. isTypeMismatch } ) {
257+ let expectedTypes = failures
258+ . compactMap { " ' \( $0. resourceObjectJsonAPIType) ' " }
259+ . joined ( separator: " , " )
260+
261+ return " Found JSON:API type ' \( foundType) ' but expected one of \( expectedTypes) "
262+ }
263+
264+ // concise error when all but failures but one are type mismatches because
265+ // we can assume the correct type was found but there was some other error:
266+ let nonTypeMismatches = failures. filter ( { !$0. cause. isTypeMismatch} )
267+ if nonTypeMismatches. count == 1 , let nonTypeMismatch = nonTypeMismatches. first {
268+ return String ( describing: nonTypeMismatch)
269+ }
270+
271+ // fall back to just describing all of the reasons it could not have been any of the available
272+ // types:
229273 return failures
230274 . enumerated ( )
231275 . map {
232- " \n Could not have been Include Type \( $0. offset + 1 ) because: \n \( $0. element) "
276+ " \n Could not have been Include Type ` \( $0. element . resourceObjectJsonAPIType ) ` because:\n \( $0. element) "
233277 } . joined ( separator: " \n " )
234278 }
235279}
0 commit comments