import Foundation struct DefaultLeadRepository: LeadRepository { let remoteService: any LeadRemoteService func leads(for salesCode: String, query: LeadQuery) async throws -> [Lead] { do { let values = try await remoteService.fetchLeads(salesCode: salesCode, query: query) let leads = try values.map { try $0.toDomain() } guard Set(leads.map(\.id)).count == leads.count else { throw LeadError.invalidResponse } return leads } catch { throw Self.map(error) } } func lead(id: String, salesCode: String) async throws -> Lead { do { let value = try await remoteService.fetchLead(id: id, salesCode: salesCode) guard value.id == id else { throw LeadError.invalidResponse } return try value.toDomain() } catch { throw Self.map(error, leadID: id) } } func setBookmark(_ bookmarked: Bool, leadID: String, salesCode: String) async throws -> Lead { do { let value = try await remoteService.setBookmark(bookmarked, id: leadID, salesCode: salesCode) guard value.id == leadID, value.isBookmarked == bookmarked else { throw LeadError.invalidResponse } return try value.toDomain() } catch { throw Self.map(error, leadID: leadID) } } static func map(_ error: Error, leadID: String? = nil) -> Error { if error is CancellationError { return CancellationError() } if let error = error as? LeadError { return error } if let error = error as? URLError { switch error.code { case .cancelled: return CancellationError() case .notConnectedToInternet, .networkConnectionLost: return LeadError.offline case .timedOut: return LeadError.timeout case .cannotFindHost, .cannotConnectToHost, .dnsLookupFailed: return LeadError.serviceUnavailable default: return LeadError.unexpected } } if let error = error as? LeadRemoteError { switch error { case .http(401): return LeadError.unauthorized case .http(403): return LeadError.forbidden case .http(404): return leadID.map { LeadError.notFound(leadID: $0) } ?? .serviceUnavailable case .http(let status) where status >= 500: return LeadError.serviceUnavailable case .malformedResponse: return LeadError.invalidResponse default: return LeadError.unexpected } } if error is DecodingError { return LeadError.invalidResponse } return LeadError.unexpected } }