Skip to content

Instantly share code, notes, and snippets.

@polac24
Last active April 16, 2018 18:41
Show Gist options
  • Save polac24/79da9649c68ee4cdc4ed5a7764c7eea3 to your computer and use it in GitHub Desktop.
Save polac24/79da9649c68ee4cdc4ed5a7764c7eea3 to your computer and use it in GitHub Desktop.
public typealias HandleAction<T> = (T) throws -> ()
public protocol ErrorHandleable: class {
func `throw`(_: Error, finally: @escaping (Bool) -> Void)
func `catch`(action: @escaping HandleAction<Error>) -> ErrorHandleable
}
public class ErrorHandler: ErrorHandleable {
private var parent: ErrorHandler?
private let action: HandleAction<Error>
convenience init(action: @escaping HandleAction<Error> = { throw $0 }) {
self.init(action: action, parent: nil)
}
private init(action: @escaping HandleAction<Error>, parent: ErrorHandler? = nil) {
self.action = action
self.parent = parent
}
public func `throw`(_ error: Error, finally: @escaping (Bool) -> Void) {
`throw`(error, previous: [], finally: finally)
}
private func `throw`(_ error: Error, previous: [ErrorHandler], finally: ((Bool) -> Void)? = nil) {
if let parent = parent {
parent.`throw`(error, previous: previous + [self], finally: finally)
return
}
serve(error, next: AnyCollection(previous.reversed()), finally: finally)
}
private func serve(_ error: Error, next: AnyCollection<ErrorHandler>, finally: ((Bool) -> Void)? = nil) {
do {
try action(error)
finally?(true)
} catch {
if let nextHandler = next.first {
nextHandler.serve(error, next: next.dropFirst(), finally: finally)
} else {
finally?(false)
}
}
}
public func `catch`(action: @escaping HandleAction<Error>) -> ErrorHandleable {
return ErrorHandler(action: action, parent: self)
}
}
//
// Protocol extensions for convenience API
public extension ErrorHandleable {
func `do`<A>(_ section: () throws -> A) {
do {
try section()
} catch {
`throw`(error)
}
}
}
public extension ErrorHandleable {
func `throw`(_ error: Error) {
`throw`(error, finally: { _ in })
}
}
public extension ErrorHandleable {
func `catch`<K:Error>(_ type: K.Type, action: @escaping HandleAction<K>) -> ErrorHandleable {
return `catch`(action: { (e) in
if let k = e as? K {
try action(k)
} else {
throw e
}
})
}
func `catch`<K:Error>(_ value: K, action: @escaping HandleAction<K>) -> ErrorHandleable where K: Equatable {
return `catch`(action: { (e) in
if let k = e as? K, k == value {
try action(k)
} else {
throw e
}
})
}
}
public extension ErrorHandleable {
func listen(action: @escaping (Error) -> ()) -> ErrorHandleable {
return `catch`(action: { e in
action(e)
throw e
})
}
func listen<K:Error>(_ type: K.Type, action: @escaping (K) -> ()) -> ErrorHandleable {
return `catch`(type, action: { e in
action(e)
throw e
})
}
func listen<K:Error>(_ value: K, action: @escaping (K) -> ()) -> ErrorHandleable where K: Equatable {
return `catch`(value, action: { e in
action(e)
throw e
})
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment