89 lines · 2.8 KB · Swift
RawDownload
import Foundation#if canImport(FoundationNetworking)import FoundationNetworking#endif public struct CrofUsageResponse: Decodable, Sendable {    public let credits: Double    public let requestsPlan: Double    public let usableRequests: Double     enum CodingKeys: String, CodingKey {        case credits        case requestsPlan = "requests_plan"        case usableRequests = "usable_requests"    }} public enum CrofUsageError: LocalizedError, Sendable, Equatable {    case missingCredentials    case networkError(String)    case apiError(Int)    case parseFailed(String)     public var errorDescription: String? {        switch self {        case .missingCredentials:            "Missing Crof API key."        case let .networkError(message):            "Crof network error: \(message)"        case let .apiError(statusCode):            "Crof API error: HTTP \(statusCode)"        case let .parseFailed(message):            "Failed to parse Crof response: \(message)"        }    }} public enum CrofUsageFetcher {    public static let usageURL = URL(string: "https://crof.ai/usage_api/")!    private static let timeoutSeconds: TimeInterval = 15     public static func fetchUsage(        apiKey: String,        session transport: any ProviderHTTPTransport = ProviderHTTPClient.shared) async throws -> CrofUsageSnapshot    {        let trimmed = apiKey.trimmingCharacters(in: .whitespacesAndNewlines)        guard !trimmed.isEmpty else {            throw CrofUsageError.missingCredentials        }         var request = URLRequest(url: self.usageURL)        request.httpMethod = "GET"        request.timeoutInterval = Self.timeoutSeconds        request.setValue("Bearer \(trimmed)", forHTTPHeaderField: "Authorization")        request.setValue("application/json", forHTTPHeaderField: "Accept")         let response: ProviderHTTPResponse        do {            response = try await transport.response(for: request)        } catch {            throw CrofUsageError.networkError(error.localizedDescription)        }         guard response.statusCode == 200 else {            throw CrofUsageError.apiError(response.statusCode)        }         return try self.parseSnapshot(response.data)    }     static func _parseSnapshotForTesting(_ data: Data) throws -> CrofUsageSnapshot {        try self.parseSnapshot(data)    }     private static func parseSnapshot(_ data: Data) throws -> CrofUsageSnapshot {        let decoded: CrofUsageResponse        do {            decoded = try JSONDecoder().decode(CrofUsageResponse.self, from: data)        } catch {            throw CrofUsageError.parseFailed(error.localizedDescription)        }         return CrofUsageSnapshot(            credits: decoded.credits,            requestsPlan: decoded.requestsPlan,            usableRequests: decoded.usableRequests,            updatedAt: Date())    }}