1 // Copyright 2022-present 650 Industries. All rights reserved.
2 
3 /**
4  An exception that may have been caused by another error.
5  */
6 public protocol ChainableException: Error, AnyObject {
7   /**
8    The direct cause of the exception.
9    */
10   var cause: Error? { get set }
11 
12   /**
13    The first error that started the chain of exceptions.
14    */
15   var rootCause: Error? { get }
16 
17   /**
18    Sets the direct cause of the exception and returns itself.
19    */
causedBynull20   func causedBy(_ error: Error?) -> Self
21 
22   /**
23    Tells whether any of the cause in chain is of given type.
24    */
25   func isCausedBy<ErrorType: Error>(_ errorType: ErrorType.Type) -> Bool
26 }
27 
28 public extension ChainableException {
29   var rootCause: Error? {
30     if let cause = cause as? ChainableException {
31       return cause.rootCause ?? cause
32     }
33     return cause
34   }
35 
36   @discardableResult
37   func causedBy(_ error: Error?) -> Self {
38     cause = error ?? cause
39     return self
40   }
41 
42   func isCausedBy<ErrorType: Error>(_ errorType: ErrorType.Type) -> Bool {
43     if cause is ErrorType {
44       return true
45     }
46     if let cause = cause as? ChainableException {
47       return cause.isCausedBy(errorType)
48     }
49     return false
50   }
51 }
52