<commit_before>//
//  RetryOperation.swift
//  Operations
//
//  Created by <NAME> on 29/12/2015.
//
//

import Foundation

/**
 RetryFailureInfo is a value type which provides 
 information related to a previously failed 
 NSOperation which it is generic over. It is used
 in conjunction with RetryOperation.
*/
public struct RetryFailureInfo<T: NSOperation> {

    /// - returns: the failed operation
    public let operation: T

    /// - returns: the errors the operation finished with.
    public let errors: [ErrorType]

    /// - returns: all the aggregate errors of previous attempts
    public let aggregateErrors: [ErrorType]

    /// - returns: the number of attempts made so far
    public let count: Int

    /**
     This is a block which can be used to add operations
     to a queue. For example, perhaps it is necessary
     to retry the task, but only until another operation
     has completed. This can be done by creating the
     operation, setting the dependency and adding it using
     this block, before responding to the RetryOperation.
     
     - returns: a block which accects var arg NSOperation instances.
    */
    public let addOperations: (NSOperation...) -> Void
}

class RetryGenerator<T: NSOperation>: GeneratorType {
    typealias Handler = RetryOperation<T>.Handler

    internal let retry: Handler
    internal var info: RetryFailureInfo<T>? = .None
    private var generator: AnyGenerator<(Delay?, T)>

    init(generator: AnyGenerator<(Delay?, T)>, retry: Handler) {
        self.generator = generator
        self.retry = retry
    }

    func next() -> (Delay?, T)? {
        guard let (delay, next) = generator.next() else { return nil }
        guard let info = info else { return (delay, next) }
        return retry(info, delay, next)
    }
}

/**
 RetryOperation is a subclass of RepeatedOperation. Like RepeatedOperation
 it is generic over type T, an NSOperation subclass. It can be used to
 automatically retry another instance of operation T if the first operation
 finishes with errors.
 
 To support effective error recovery, in addition to a (Delay?, T) generator
 RetryOperation is initialized with a block. The block will receive failure
 info, in addition to the next result (if not nil) of the operation generator.
 The block must return (Delay?, T)?.
 
 Therefore consumers can inspect the failure info, and adjust the Delay, or
 operation before returning it. To finish, the block can return .None
*/
public class RetryOperation<T: NSOperation>: RepeatedOperation<T> {
    public typealias FailureInfo = RetryFailureInfo<T>
    public typealias Handler = (RetryFailureInfo<T>, Delay?, T) -> (Delay?, T)?

    let retry: RetryGenerator<T>

    /**
     The designated initializer
     
     Creates an operation which will retry executing operations in the face
     of errors.
     
     - parameter maxCount: an optional Int, which defaults to .None. If not nil, this is
     the maximum number of operations which will be executed.
     - parameter generator: the generator of (Delay?, T) values.
     - parameter retry: a Handler block type, can be used to inspect aggregated error to
     adjust the next delay and Operation.

    */
    public init(maxCount max: Int?, generator: AnyGenerator<(Delay?, T)>, retry block: Handler) {
        retry = RetryGenerator(generator: generator, retry: block)
        super.init(maxCount: max, generator: anyGenerator(retry))
        name = "Retry Operation"
    }

    /**
     A convenience initializer, which accepts two generators, one for the delay and another for
     the operation, in addition to a retry handler block

     - parameter maxCount: an optional Int, which defaults to .None. If not nil, this is
     the maximum number of operations which will be executed.
     - parameter delay: a generator with Delay element.
     - parameter generator: a generator with T element.
     - parameter retry: a Handler block type, can be used to inspect aggregated error to
     adjust the next delay and Operation.

     */
    public convenience init<D, G where D: GeneratorType, D.Element == Delay, G: GeneratorType, G.Element == T>(maxCount max: Int?, delay: D, generator: G, retry: Handler) {
        let tuple = TupleGenerator(primary: generator, secondary: delay)
        self.init(maxCount: max, generator: anyGenerator(tuple), retry: retry)
    }

    /**
     A convenience initializer with wait strategy and generic operation generator.
     This is useful where another system can be responsible for vending instances of
     the custom operation. Typically there may be some state involved in such a Generator. e.g.

     The wait strategy is useful if say, you want to repeat the operations with random
     delays, or exponential backoff. These standard schemes and be easily expressed.

     ```swift
     class MyOperationGenerator: GeneratorType {
         func next() -> MyOperation? {
              // etc
         }
     }

     let operation = RetryOperation(
         maxCount: 3, 
         strategy: .Random((0.1, 1.0)),
         generator: MyOperationGenerator()
     ) { info, delay, op in
         // inspect failure info
         return (delay, op)
     }
     ```

     - parameter maxCount: an optional Int, which defaults to 5.
     - parameter strategy: a WaitStrategy which defaults to a 0.1 second fixed interval.
     - parameter [unnamed] generator: a generic generator which has an Element equal to T.
     - parameter retry: a Handler block type, can be used to inspect aggregated error to
     adjust the next delay and Operation. This defaults to pass through the delay and 
     operation regardless of error info.

     */
    public convenience init<G where G: GeneratorType, G.Element == T>(maxCount max: Int? = 5, strategy: WaitStrategy = .Fixed(0.1), _ generator: G, retry: Handler = { ($1, $2) }) {
        self.init(maxCount: max, delay: GeneratorMap(strategy.generator()) { Delay.By($0) }, generator: generator, retry: retry)
    }

    public override func operationDidFinish(operation: NSOperation, withErrors errors: [ErrorType]) {
        if !errors.isEmpty, let op = operation as? T {
            retry.info = createFailureInfo(op, errors: errors)
            addNextOperation()
        }
    }

    internal func createFailureInfo(operation: T, errors: [ErrorType]) -> RetryFailureInfo<T> {
        return RetryFailureInfo(
            operation: operation,
            errors: errors,
            aggregateErrors: aggregateErrors,
            count: count,
            addOperations: addOperations
        )
    }
}
<commit_msg>[OPR-167]: Refactors Retry initializers<commit_after>//
//  RetryOperation.swift
//  Operations
//
//  Created by <NAME> on 29/12/2015.
//
//

import Foundation

/**
 RetryFailureInfo is a value type which provides 
 information related to a previously failed 
 NSOperation which it is generic over. It is used
 in conjunction with RetryOperation.
*/
public struct RetryFailureInfo<T: NSOperation> {

    /// - returns: the failed operation
    public let operation: T

    /// - returns: the errors the operation finished with.
    public let errors: [ErrorType]

    /// - returns: all the aggregate errors of previous attempts
    public let aggregateErrors: [ErrorType]

    /// - returns: the number of attempts made so far
    public let count: Int

    /**
     This is a block which can be used to add operations
     to a queue. For example, perhaps it is necessary
     to retry the task, but only until another operation
     has completed. This can be done by creating the
     operation, setting the dependency and adding it using
     this block, before responding to the RetryOperation.
     
     - returns: a block which accects var arg NSOperation instances.
    */
    public let addOperations: (NSOperation...) -> Void
}

class RetryGenerator<T: NSOperation>: GeneratorType {
    typealias Handler = RetryOperation<T>.Handler

    internal let retry: Handler
    internal var info: RetryFailureInfo<T>? = .None
    private var generator: AnyGenerator<(Delay?, T)>

    init(generator: AnyGenerator<(Delay?, T)>, retry: Handler) {
        self.generator = generator
        self.retry = retry
    }

    func next() -> (Delay?, T)? {
        guard let (delay, next) = generator.next() else { return nil }
        guard let info = info else { return (delay, next) }
        return retry(info, delay, next)
    }
}

/**
 RetryOperation is a subclass of RepeatedOperation. Like RepeatedOperation
 it is generic over type T, an NSOperation subclass. It can be used to
 automatically retry another instance of operation T if the first operation
 finishes with errors.
 
 To support effective error recovery, in addition to a (Delay?, T) generator
 RetryOperation is initialized with a block. The block will receive failure
 info, in addition to the next result (if not nil) of the operation generator.
 The block must return (Delay?, T)?.
 
 Therefore consumers can inspect the failure info, and adjust the Delay, or
 operation before returning it. To finish, the block can return .None
*/
public class RetryOperation<T: NSOperation>: RepeatedOperation<T> {
    public typealias FailureInfo = RetryFailureInfo<T>
    public typealias Handler = (RetryFailureInfo<T>, Delay?, T) -> (Delay?, T)?

    let retry: RetryGenerator<T>

    /**
     A designated initializer
     
     Creates an operation which will retry executing operations in the face
     of errors.
     
     - parameter maxCount: an optional Int, which defaults to .None. If not nil, this is
     the maximum number of operations which will be executed.
     - parameter generator: the generator of (Delay?, T) values.
     - parameter retry: a Handler block type, can be used to inspect aggregated error to
     adjust the next delay and Operation.

    */
    public init(maxCount max: Int?, generator: AnyGenerator<(Delay?, T)>, retry block: Handler) {
        retry = RetryGenerator(generator: generator, retry: block)
        super.init(maxCount: max, generator: anyGenerator(retry))
        name = "Retry Operation"
    }

    /**
     A designated initializer, which accepts two generators, one for the delay and another for
     the operation, in addition to a retry handler block

     - parameter maxCount: an optional Int, which defaults to .None. If not nil, this is
     the maximum number of operations which will be executed.
     - parameter delay: a generator with Delay element.
     - parameter generator: a generator with T element.
     - parameter retry: a Handler block type, can be used to inspect aggregated error to
     adjust the next delay and Operation.

     */
    public init<D, G where D: GeneratorType, D.Element == Delay, G: GeneratorType, G.Element == T>(maxCount max: Int?, delay: D, generator: G, retry block: Handler) {
        let tuple = TupleGenerator(primary: generator, secondary: delay)
        retry = RetryGenerator(generator: anyGenerator(tuple), retry: block)
        super.init(maxCount: max, generator: anyGenerator(retry))
        name = "Retry Operation"
    }

    /**
     A convenience initializer with wait strategy and generic operation generator.
     This is useful where another system can be responsible for vending instances of
     the custom operation. Typically there may be some state involved in such a Generator. e.g.

     The wait strategy is useful if say, you want to repeat the operations with random
     delays, or exponential backoff. These standard schemes and be easily expressed.

     ```swift
     class MyOperationGenerator: GeneratorType {
         func next() -> MyOperation? {
              // etc
         }
     }

     let operation = RetryOperation(
         maxCount: 3, 
         strategy: .Random((0.1, 1.0)),
         generator: MyOperationGenerator()
     ) { info, delay, op in
         // inspect failure info
         return (delay, op)
     }
     ```

     - parameter maxCount: an optional Int, which defaults to 5.
     - parameter strategy: a WaitStrategy which defaults to a 0.1 second fixed interval.
     - parameter [unnamed] generator: a generic generator which has an Element equal to T.
     - parameter retry: a Handler block type, can be used to inspect aggregated error to
     adjust the next delay and Operation. This defaults to pass through the delay and 
     operation regardless of error info.

     */
    public convenience init<G where G: GeneratorType, G.Element == T>(maxCount max: Int? = 5, strategy: WaitStrategy = .Fixed(0.1), _ generator: G, retry: Handler = { ($1, $2) }) {
        self.init(maxCount: max, delay: MapGenerator(strategy.generator()) { Delay.By($0) }, generator: generator, retry: retry)
    }

    public override func operationDidFinish(operation: NSOperation, withErrors errors: [ErrorType]) {
        if !errors.isEmpty, let op = operation as? T {
            retry.info = createFailureInfo(op, errors: errors)
            addNextOperation()
        }
    }

    internal func createFailureInfo(operation: T, errors: [ErrorType]) -> RetryFailureInfo<T> {
        return RetryFailureInfo(
            operation: operation,
            errors: errors,
            aggregateErrors: aggregateErrors,
            count: count,
            addOperations: addOperations
        )
    }
}

{'fraction_non_alphanumeric': 0.07911344853493614, 'fraction_numerical': 0.0035311795642374156, 'mean_word_length': 2.8841552378173327, 'pattern_counts': {'":': 0, '<': 35, '<?xml version=': 0, '>': 45, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6946741', 'n_tokens_mistral': 3447, 'n_tokens_neox': 3339, 'n_words': 1519}