/*:#localized(key: "FailingWithAValueErrorPair")
 # When the callback can fail: a `(value, error)` pair

 Older Objective-C-era APIs hand you *both* an optional value and an optional
 error, and it's on you to sort them out. There's no `resume(with:)`
 shortcut here: you branch by hand and resume exactly one way.
 */
//#-editable-code
import Foundation
import PlaygroundSupport

PlaygroundPage.current.needsIndefiniteExecution = true

enum InventoryError: Error {
    case scaleOffline
    case emptyReading
}

// Legacy API: (value?, error?), the classic two-optionals callback.
func countBeansLeft(
    completion: @escaping @Sendable (Int?, Error?) -> Void
) {
    DispatchQueue.global().asyncAfter(deadline: .now() + 0.2) {
        completion(1_240, nil)   // grams of beans; nil error = success
    }
}

func beansLeft() async throws -> Int {
    try await withCheckedThrowingContinuation { continuation in
        countBeansLeft { grams, error in
            if let error {
                continuation.resume(throwing: error)
            } else if let grams {
                continuation.resume(returning: grams)
            } else {
                // Neither value nor error: the API misbehaved.
                // We still MUST resume, or we'd hang forever.
                continuation.resume(throwing: InventoryError.emptyReading)
            }
        }
    }
}

Task {
    do {
        let grams = try await beansLeft()
        print("Beans left: \(grams) g")
    } catch {
        print("Couldn't weigh the beans: \(error)")
    }
    PlaygroundPage.current.finishExecution()
}
//#-end-editable-code
/*:#localized(key: "FailingWithAValueErrorPair-2")
 **Watch it run:** you'll see `Beans left: 1240 g`. Notice the third branch:
 even when the callback gives you *neither* a value nor an error (a shape the
 compiler can't rule out) you resume with an error anyway. Skipping that
 branch is a hang waiting to happen, which is exactly the rule we turn to
 next.

 * callout(Try it): Change the callback to `completion(nil, nil)`. The
 "neither" branch now fires, `beansLeft()` throws `emptyReading`, and you
 land in the `catch` instead of hanging.

 [Previous](@previous)  |  page 4 of 8  |  [Next: The Golden Rule](@next)
 */
