Skip to content

Commit

Permalink
Adjust error handling
Browse files Browse the repository at this point in the history
  • Loading branch information
ffried committed Sep 24, 2020
1 parent 3989d7e commit 6c6e1b8
Show file tree
Hide file tree
Showing 7 changed files with 47 additions and 125 deletions.
10 changes: 3 additions & 7 deletions Sources/GCDCoreOperations/Base Classes/Operation.swift
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ open class Operation {
conditionGroup.notify(queue: queue ?? .global()) {
if self.state.isCancelled && self.errors.isEmpty {
// TODO: Do we really need this? We could just assume it was cancelled for good.
self.aggregate(error: ConditionError(name: "AnyCondition"))
self.aggregate(error: AnyConditionFailed())
}

let failures = results.compactMap { $0?.error }
Expand Down Expand Up @@ -280,10 +280,6 @@ internal extension Operation {
}
}

// MARK: - Extensions
fileprivate extension ConditionError {
init(name: String, errorInformation: ErrorInformation? = nil) {
self.conditionName = name
self.information = errorInformation
}
fileprivate struct AnyConditionFailed: AnyConditionError {
var conditionName: String { "AnyCondition" }
}
38 changes: 13 additions & 25 deletions Sources/GCDCoreOperations/Condition/OperationCondition.swift
Original file line number Diff line number Diff line change
Expand Up @@ -26,18 +26,17 @@ public protocol OperationCondition {
}

/// An error representing a failed condition.
public struct ConditionError: Error, Equatable {
public let conditionName: String
public let information: ErrorInformation?

public init<Condition: OperationCondition>(condition: Condition, errorInformation: ErrorInformation? = nil) {
self.conditionName = Condition.name
self.information = errorInformation
}

public static func ==(lhs: Self, rhs: Self) -> Bool {
lhs.conditionName == rhs.conditionName
}
public protocol AnyConditionError: Error {
var conditionName: String { get }
}

public protocol ConditionError: AnyConditionError {
associatedtype Condition: OperationCondition
}

extension ConditionError {
@inlinable
public var conditionName: String { Condition.name }
}


Expand All @@ -49,25 +48,14 @@ public struct ConditionError: Error, Equatable {
/// The associated `ConditionError` describes what failure happened during evaluation.
public enum OperationConditionResult {
case satisfied
case failed(ConditionError)
case failed(AnyConditionError)

var error: ConditionError? {
var error: AnyConditionError? {
switch self {
case .failed(let error):
return error
default:
return nil
}
}

public static func ==(lhs: Self, rhs: Self) -> Bool {
switch (lhs, rhs) {
case (.satisfied, .satisfied):
return true
case (.failed(let lError), .failed(let rError)):
return lError == rError
default:
return false
}
}
}
54 changes: 0 additions & 54 deletions Sources/GCDCoreOperations/OperationErrors.swift

This file was deleted.

30 changes: 14 additions & 16 deletions Sources/GCDOperations/Conditions/Negated.swift
Original file line number Diff line number Diff line change
@@ -1,42 +1,40 @@
import class GCDCoreOperations.Operation
import struct GCDCoreOperations.ErrorInformation
import protocol GCDCoreOperations.OperationCondition

extension ErrorInformation.Key {
public static var negatedCondition: ErrorInformation.Key<OperationCondition> { .init(rawValue: "NegatedCondition") }
}

/**
A simple condition that negates the evaluation of another condition.
This is useful (for example) if you want to only execute an operation if the
network is NOT reachable.
*/
public struct NegatedCondition<Condition: OperationCondition>: OperationCondition {
public static var name: String { return "Not<\(Condition.name)>" }
public static var isMutuallyExclusive: Bool { return Condition.isMutuallyExclusive }
public struct NegatedCondition<ConditionToNegate: OperationCondition>: OperationCondition {
public struct Error: ConditionError {
public typealias Condition = NegatedCondition<ConditionToNegate>

public let negatedCondition: ConditionToNegate
}

public static var name: String { "Not<\(ConditionToNegate.name)>" }
public static var isMutuallyExclusive: Bool { ConditionToNegate.isMutuallyExclusive }

private let condition: Condition
private let condition: ConditionToNegate

public init(condition: Condition) {
public init(condition: ConditionToNegate) {
self.condition = condition
}

public func dependency(for operation: GCDCoreOperations.Operation) -> GCDCoreOperations.Operation? {
public func dependency(for operation: GCDOperation) -> GCDOperation? {
condition.dependency(for: operation)
}

public func evaluate(for operation: GCDCoreOperations.Operation, completion: @escaping (OperationConditionResult) -> ()) {
public func evaluate(for operation: GCDOperation, completion: @escaping (OperationConditionResult) -> ()) {
condition.evaluate(for: operation) {
switch $0 {
case .failed(_):
// If the composed condition failed, then this one succeeded.
completion(.satisfied)
case .satisfied:
// If the composed condition succeeded, then this one failed.
let info = ErrorInformation(key: .negatedCondition, value: self.condition)
let error = ConditionError(condition: self, errorInformation: info)

completion(.failed(error))
completion(.failed(Error(negatedCondition: condition)))
}
}
}
Expand Down
19 changes: 8 additions & 11 deletions Sources/GCDOperations/Conditions/NoCancelledDependencies.swift
Original file line number Diff line number Diff line change
@@ -1,18 +1,17 @@
import typealias GCDCoreOperations.GCDOperation
import struct GCDCoreOperations.ErrorInformation
import protocol GCDCoreOperations.OperationCondition

extension ErrorInformation.Key {
public static var cancelledDependencies: ErrorInformation.Key<ContiguousArray<GCDOperation>> {
.init(rawValue: "CancelledDependencies")
}
}

/**
A condition that specifies that every dependency must have run.
If any dependency was cancelled, the target operation will fail.
*/
public struct NoCancelledDependencies: OperationCondition {
public struct Error: ConditionError {
public typealias Condition = NoCancelledDependencies

public let cancelledDependencies: ContiguousArray<GCDOperation>
}

public static let name = "NoCancelledDependencies"
public static let isMutuallyExclusive = false

Expand All @@ -22,13 +21,11 @@ public struct NoCancelledDependencies: OperationCondition {

public func evaluate(for operation: GCDOperation, completion: @escaping (OperationConditionResult) -> ()) {
// Verify that all of the dependencies executed.
let cancelled = ContiguousArray(operation.dependencies.filter { $0.isCancelled })
let cancelled = ContiguousArray(operation.dependencies.lazy.filter(\.isCancelled))

if !cancelled.isEmpty {
// At least one dependency was cancelled; the condition was not satisfied.
let info = ErrorInformation(key: .cancelledDependencies, value: cancelled)
let error = ConditionError(condition: self, errorInformation: info)
completion(.failed(error))
completion(.failed(Error(cancelledDependencies: cancelled)))
} else {
completion(.satisfied)
}
Expand Down
17 changes: 7 additions & 10 deletions Sources/GCDOperations/Conditions/NoFailedDependencies.swift
Original file line number Diff line number Diff line change
@@ -1,18 +1,17 @@
import typealias GCDCoreOperations.GCDOperation
import struct GCDCoreOperations.ErrorInformation
import protocol GCDCoreOperations.OperationCondition

extension ErrorInformation.Key {
public static var failedDependencies: ErrorInformation.Key<ContiguousArray<GCDOperation>> {
.init(rawValue: "FailedDependencies")
}
}

/**
A condition that specifies that every dependency must have succeeded.
If any dependency has errors, the target operation will fail as well.
*/
public struct NoFailedDependencies: OperationCondition {
public struct Error: ConditionError {
public typealias Condition = NoFailedDependencies

public let failedDependencies: ContiguousArray<GCDOperation>
}

public static let name = "NoFailedDependencies"
public static let isMutuallyExclusive = false

Expand All @@ -26,9 +25,7 @@ public struct NoFailedDependencies: OperationCondition {

if !failed.isEmpty {
// At least one dependency was cancelled; the condition was not satisfied.
let info = ErrorInformation(key: .failedDependencies, value: failed)
let error = ConditionError(condition: self, errorInformation: info)
completion(.failed(error))
completion(.failed(Error(failedDependencies: failed)))
} else {
completion(.satisfied)
}
Expand Down
4 changes: 2 additions & 2 deletions Sources/GCDOperations/Operations/DelayOperation.swift
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,8 @@ public final class DelayOperation: GCDOperation {
finish()
return
}
DispatchQueue.global().asyncAfter(deadline: .now() + interval) {
guard !self.isCancelled else { return }
DispatchQueue.global().asyncAfter(deadline: .now() + interval) { [weak self] in
guard let self = self, !self.isCancelled else { return }
self.finish()
}
}
Expand Down

0 comments on commit 6c6e1b8

Please sign in to comment.