55// Created by Finn Behrens on 07.08.21.
66//
77
8+ import AnyCodable
89import Foundation
910
1011public enum MatrixCommonErrorCode : String , Error , Codable {
@@ -250,6 +251,13 @@ public extension MatrixErrorCode {
250251}
251252
252253public struct MatrixServerError : Error , Codable {
254+ public init ( errcode: MatrixErrorCode , error: String , code: Int ? = nil , extraInfo: [ String : AnyCodable ] = [ : ] ) {
255+ self . errcode = errcode
256+ self . error = error
257+ self . code = code
258+ self . extraInfo = extraInfo
259+ }
260+
253261 /// Error code
254262 public var errcode : MatrixErrorCode
255263
@@ -259,30 +267,128 @@ public struct MatrixServerError: Error, Codable {
259267 /// HTTP status code
260268 public var code : Int ?
261269
270+ public var interactiveAuth : MatrixInteractiveAuth ?
271+
272+ public var extraInfo : [ String : AnyCodable ]
273+
262274 // TODO: extra data
263275
264276 public init ( json: Data , code: Int ? = nil ) throws {
265277 let decoder = JSONDecoder ( )
278+ decoder. userInfo [ . matrixErrorHttpCode] = code
266279
267280 do {
268281 self = try decoder. decode ( Self . self, from: json)
269282 } catch {
270- throw MatrixInvalidError ( data: json, code: code)
283+ throw MatrixServerError (
284+ errcode: . Unknown,
285+ error: error. localizedDescription,
286+ code: code,
287+ extraInfo: [ " json " : . init( json) ]
288+ )
271289 }
272290 self . code = code
273291 }
274292}
275293
276- public struct MatrixInvalidError : Error , LocalizedError {
277- public var data : Data
278- public var code : Int ?
294+ public extension MatrixServerError {
295+ internal enum KnownCodingKeys : String , CodingKey , CaseIterable {
296+ case errcode
297+ case error
279298
280- public init ( data: Data , code: Int ? = nil ) {
281- self . data = data
282- self . code = code
299+ static let extraIgnoreValues = [
300+ " session " ,
301+ " flows " ,
302+ " params " ,
303+ " completed " ,
304+ ]
305+
306+ static func doesNotContain( _ key: DynamicCodingKeys ) -> Bool {
307+ !Self. allCases. map ( \. stringValue) . contains ( key. stringValue) && !Self. extraIgnoreValues
308+ . contains ( key. stringValue)
309+ }
310+ }
311+
312+ internal struct DynamicCodingKeys : CodingKey {
313+ var stringValue : String
314+ init ? ( stringValue: String ) {
315+ self . stringValue = stringValue
316+ }
317+
318+ // not used here, but a protocol requirement
319+ var intValue : Int ?
320+ init ? ( intValue _: Int ) {
321+ nil
322+ }
283323 }
284324
285- var localisedDescription : String {
286- NSLocalizedString ( " Failed to parse error result for code \( code ?? - 1 ) " , comment: " MatrixInvalidError " )
325+ init ( from decoder: Decoder ) throws {
326+ let container = try decoder. container ( keyedBy: KnownCodingKeys . self)
327+ errcode = try container. decodeIfPresent ( MatrixErrorCode . self, forKey: . errcode) ?? . Unknown
328+ error = try container. decodeIfPresent ( String . self, forKey: . error) ?? " "
329+
330+ extraInfo = [ : ]
331+ let extraContainer = try decoder. container ( keyedBy: DynamicCodingKeys . self)
332+
333+ for key in extraContainer. allKeys where KnownCodingKeys . doesNotContain ( key) {
334+ let decoded = try extraContainer. decode (
335+ AnyCodable . self,
336+ forKey: DynamicCodingKeys ( stringValue: key. stringValue) !
337+ )
338+ self . extraInfo [ key. stringValue] = decoded
339+ }
340+
341+ guard let code = decoder. userInfo [ . matrixErrorHttpCode] as? Int ,
342+ code == 401
343+ else {
344+ return
345+ }
346+
347+ do {
348+ interactiveAuth = try MatrixInteractiveAuth ( from: decoder)
349+ } catch {
350+ // don't care if it fails
351+ }
352+ }
353+
354+ func encode( to encoder: Encoder ) throws {
355+ var container = encoder. container ( keyedBy: KnownCodingKeys . self)
356+ try container. encode ( errcode, forKey: . errcode)
357+ try container. encode ( error, forKey: . error)
358+
359+ var extraContainer = encoder. container ( keyedBy: DynamicCodingKeys . self)
360+ for (name, value) in extraInfo {
361+ try extraContainer. encode ( value, forKey: . init( stringValue: name) !)
362+ }
363+ }
364+ }
365+
366+ public extension MatrixServerError {
367+ var is401 : Bool {
368+ errcode == . Unauthorized && code == 401
369+ }
370+
371+ var is404 : Bool {
372+ errcode == . NotFound && code == 404
373+ }
374+
375+ var isTokenError : Bool {
376+ errcode == . UnknownToken || errcode == . MissingToken
377+ }
378+
379+ var isLimitexceededError : Bool {
380+ code == 429 && errcode == . LimitExceeded
381+ }
382+
383+ var shouldbeRetried : Bool {
384+ // Investigate network error codes
385+ isLimitexceededError
386+ }
387+ }
388+
389+ extension CodingUserInfoKey {
390+ /// The key used to determine the types of `MatrixEvent` that can be decoded.
391+ static var matrixErrorHttpCode : CodingUserInfoKey {
392+ CodingUserInfoKey ( rawValue: " MatrixClient.ErrorHttpCode " ) !
287393 }
288394}
0 commit comments