diff --git a/Package.swift b/Package.swift index 0a5566e..30b44ca 100644 --- a/Package.swift +++ b/Package.swift @@ -1,4 +1,4 @@ -// swift-tools-version: 5.8 +// swift-tools-version: 5.10 // The swift-tools-version declares the minimum version of Swift required to build this package. import PackageDescription diff --git a/README.md b/README.md index cb5c2ab..04ee341 100644 --- a/README.md +++ b/README.md @@ -138,9 +138,9 @@ do { let models = try await modelRetriever.openAI(apiKey: "your-api-key") } catch let error as AIModelRetrieverError { switch error { - case .serverError(let message): + case .serverError(let statusCode, let message): // Handle server-side errors (e.g., invalid API key, rate limits) - print("Server Error: \(message)") + print("Server Error [\(statusCode)]: \(message)") case .networkError(let error): // Handle network-related errors (e.g., no internet connection) print("Network Error: \(error.localizedDescription)") diff --git a/Sources/AIModelRetriever/AIModelRetriever.swift b/Sources/AIModelRetriever/AIModelRetriever.swift index 9d940a7..e3024a8 100644 --- a/Sources/AIModelRetriever/AIModelRetriever.swift +++ b/Sources/AIModelRetriever/AIModelRetriever.swift @@ -19,12 +19,17 @@ public struct AIModelRetriever: Sendable { do { let (data, response) = try await URLSession.shared.data(for: request) + guard let httpResponse = response as? HTTPURLResponse else { + throw AIModelRetrieverError.serverError(statusCode: 0, message: response.description) + } + + // Check for API errors first, as they might come with 200 status if let errorResponse = try? JSONDecoder().decode(E.self, from: data) { - throw AIModelRetrieverError.serverError(errorResponse.errorMessage) + throw AIModelRetrieverError.serverError(statusCode: httpResponse.statusCode, message: errorResponse.errorMessage) } guard let httpResponse = response as? HTTPURLResponse, 200...299 ~= httpResponse.statusCode else { - throw AIModelRetrieverError.serverError(response.description) + throw AIModelRetrieverError.serverError(statusCode: httpResponse.statusCode, message: response.description) } return try JSONDecoder().decode(T.self, from: data) diff --git a/Sources/AIModelRetriever/AIModelRetrieverError.swift b/Sources/AIModelRetriever/AIModelRetrieverError.swift index 6c89d92..5aac182 100644 --- a/Sources/AIModelRetriever/AIModelRetrieverError.swift +++ b/Sources/AIModelRetriever/AIModelRetrieverError.swift @@ -7,35 +7,25 @@ import Foundation -/// An enum that represents errors that can occur during AI model retrieval. +/// An enum that represents errors that can occur during model retrieval. public enum AIModelRetrieverError: Error, Sendable { - /// A case that represents a server-side error response. + /// An error that occurs during JSON decoding. /// - /// - Parameter message: The error message from the server. - case serverError(String) + /// - Parameter error: The underlying decoding error. + case decodingError(Error) - /// A case that represents a network-related error. + /// An error that occurs during network operations. /// /// - Parameter error: The underlying network error. case networkError(Error) - /// A case that represents a decoding error. - case decodingError(Error) + /// An error returned by the server. + /// + /// - Parameters: + /// - statusCode: The HTTP status code returned by the server. + /// - message: The error message received from the server. + case serverError(statusCode: Int, message: String) - /// A case that represents a request has been canceled. + /// An error that occurs when the request is cancelled. case cancelled - - /// A localized message that describes the error. - public var errorDescription: String? { - switch self { - case .serverError(let error): - return error - case .networkError(let error): - return error.localizedDescription - case .decodingError(let error): - return error.localizedDescription - case .cancelled: - return "Request was cancelled" - } - } } diff --git a/Sources/AIModelRetriever/Documentation.docc/Documentation.md b/Sources/AIModelRetriever/Documentation.docc/Documentation.md index 8eb606f..098867f 100644 --- a/Sources/AIModelRetriever/Documentation.docc/Documentation.md +++ b/Sources/AIModelRetriever/Documentation.docc/Documentation.md @@ -107,9 +107,9 @@ do { let models = try await modelRetriever.openAI(apiKey: "your-api-key") } catch let error as AIModelRetrieverError { switch error { - case .serverError(let message): + case .serverError(let statusCode, let message): // Handle server-side errors (e.g., invalid API key, rate limits) - print("Server Error: \(message)") + print("Server Error [\(statusCode)]: \(message)") case .networkError(let error): // Handle network-related errors (e.g., no internet connection) print("Network Error: \(error.localizedDescription)") diff --git a/Tests/AIModelRetrieverTests/AIModelRetrieverTests.swift b/Tests/AIModelRetrieverTests/AIModelRetrieverTests.swift index 4463b71..5a11b83 100644 --- a/Tests/AIModelRetrieverTests/AIModelRetrieverTests.swift +++ b/Tests/AIModelRetrieverTests/AIModelRetrieverTests.swift @@ -14,8 +14,8 @@ final class AIModelRetrieverTests: XCTestCase { override func setUp() { super.setUp() - URLProtocol.registerClass(URLProtocolMock.self) retriever = AIModelRetriever() + URLProtocol.registerClass(URLProtocolMock.self) } override func tearDown() { @@ -155,7 +155,8 @@ extension AIModelRetrieverTests { XCTFail("Expected serverError to be thrown") } catch let error as AIModelRetrieverError { switch error { - case .serverError(let message): + case .serverError(let statusCode, let message): + XCTAssertEqual(statusCode, 401) XCTAssertEqual(message, "Invalid API key provided") default: XCTFail("Expected serverError but got \(error)") @@ -182,7 +183,8 @@ extension AIModelRetrieverTests { XCTFail("Expected serverError to be thrown") } catch let error as AIModelRetrieverError { switch error { - case .serverError(let message): + case .serverError(let statusCode, let message): + XCTAssertEqual(statusCode, 200) XCTAssertEqual(message, "An error occurred") default: XCTFail("Expected serverError but got \(error)")