import Foundation

/// Type-erasing encapsulator for `Swift.Error` that gives `Equatable`
/// comformance to any error. Ideal for storing an `Error` in Reducer State.
public struct AnyError: Error, Equatable {
    public private(set) var underlying: Error

    public init(_ underlying: Error) {
        self.underlying = underlying
    }

    public static func == (lhs: Self, rhs: Self) -> Bool {
        guard type(of: lhs) == type(of: rhs) else {
            return false
        }

        // The compiler gives a false positive warning on the casting below
        // See more: https://github.com/swiftlang/swift/issues/70114
        if let lhsEquatable = lhs.underlying as? any Equatable,
           let rhsEquatable = rhs.underlying as? any Equatable
        {
            return lhsEquatable.isEqual(to: rhsEquatable)
        }

        // For types that do not conform to Equatable, localizedDescription
        // can be used as the comparator.
        return lhs.localizedDescription == rhs.localizedDescription
    }
}

public extension Error {
    func erasedToAnyError() -> AnyError {
        AnyError(self)
    }
}

private extension Equatable {
    func isEqual(to other: any Equatable) -> Bool {
        guard let other = other as? Self else {
            return false
        }
        return self == other
    }
}
