diff --git a/.github/workflows/doc.yml b/.github/workflows/doc.yml index f54ffdc..925b17f 100644 --- a/.github/workflows/doc.yml +++ b/.github/workflows/doc.yml @@ -6,7 +6,7 @@ on: - main jobs: - test: + doc: runs-on: macos-latest steps: diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 9789802..7090a36 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -17,7 +17,7 @@ jobs: uses: actions/checkout@v2 - name: Lint code - uses: swiftlint lint + run: swiftlint lint - name: Run tests run: swift test --enable-test-discovery diff --git a/.swiftlint.yml b/.swiftlint.yml index be23d4c..1200263 100644 --- a/.swiftlint.yml +++ b/.swiftlint.yml @@ -16,5 +16,7 @@ opt_in_rules: - empty_count - indentation_width -line_length: 120 -warning_threshold: 0 +line_length: + warning: 120 + ignores_comments: true +warning_threshold: 1 diff --git a/Sources/SimpleHTTP/MultipartForm/MultipartFormData.swift b/Sources/SimpleHTTP/MultipartForm/MultipartFormData.swift index 6cdd233..780b725 100644 --- a/Sources/SimpleHTTP/MultipartForm/MultipartFormData.swift +++ b/Sources/SimpleHTTP/MultipartForm/MultipartFormData.swift @@ -176,16 +176,25 @@ public struct MultipartFormData { private func mimeType(from url: URL) -> String { if #available(iOS 14.0, *), #available(macOS 11.0, *) { guard let type = UTType(filenameExtension: url.pathExtension), let mime = type.preferredMIMEType else { - return HTTPContentType.octetStream.value + return HTTPContentType.octetStream.value } + return mime - } else { - if let id = UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, url.pathExtension as CFString, nil)?.takeRetainedValue(), - let contentType = UTTypeCopyPreferredTagWithClass(id, kUTTagClassMIMEType)?.takeRetainedValue() { - return contentType as String - } - return HTTPContentType.octetStream.value } + + let contentType = UTTypeCreatePreferredIdentifierForTag( + kUTTagClassFilenameExtension, + url.pathExtension as CFString, + nil + ) + .flatMap { UTTypeCopyPreferredTagWithClass($0.takeRetainedValue(), kUTTagClassMIMEType) } + .map { $0.takeRetainedValue() } + + if let contentType = contentType { + return contentType as String + } + + return HTTPContentType.octetStream.value } } diff --git a/Sources/SimpleHTTP/MultipartForm/MultipartFormDataEncoder.swift b/Sources/SimpleHTTP/MultipartForm/MultipartFormDataEncoder.swift index d2f1a60..d74378c 100644 --- a/Sources/SimpleHTTP/MultipartForm/MultipartFormDataEncoder.swift +++ b/Sources/SimpleHTTP/MultipartForm/MultipartFormDataEncoder.swift @@ -20,17 +20,17 @@ struct MultipartFormDataEncoder { var encoded = Data() if var first = bodyParts.first { - first.hasInitialBoundary = true - bodyParts[0] = first + first.hasInitialBoundary = true + bodyParts[0] = first } if var last = bodyParts.last { - last.hasFinalBoundary = true - bodyParts[bodyParts.count - 1] = last + last.hasFinalBoundary = true + bodyParts[bodyParts.count - 1] = last } for bodyPart in bodyParts { - encoded.append(try encodeBodyPart(bodyPart)) + encoded.append(try encodeBodyPart(bodyPart)) } return encoded diff --git a/Sources/SimpleHTTP/MultipartForm/URLRequest+Multipart.swift b/Sources/SimpleHTTP/MultipartForm/URLRequest+Multipart.swift index b196c55..ffec1df 100644 --- a/Sources/SimpleHTTP/MultipartForm/URLRequest+Multipart.swift +++ b/Sources/SimpleHTTP/MultipartForm/URLRequest+Multipart.swift @@ -6,10 +6,10 @@ import FoundationNetworking extension URLRequest { public mutating func multipartBody(_ body: MultipartFormData) throws { - var multipartEncode = MultipartFormDataEncoder(body: body) + var multipartEncode = MultipartFormDataEncoder(body: body) - httpBody = try multipartEncode.encode() + httpBody = try multipartEncode.encode() - setHeaders([.contentType: HTTPContentType.multipart(boundary: body.boundary).value]) + setHeaders([.contentType: HTTPContentType.multipart(boundary: body.boundary).value]) } } diff --git a/Sources/SimpleHTTP/Request/Path.swift b/Sources/SimpleHTTP/Request/Path.swift index 3f4fc74..3cfc1fd 100644 --- a/Sources/SimpleHTTP/Request/Path.swift +++ b/Sources/SimpleHTTP/Request/Path.swift @@ -37,7 +37,7 @@ public struct Path: Equatable, ExpressibleByStringLiteral, ExpressibleByStringIn self.init(value: stringInterpolation.description) } - public static func ==(lhs: Path, rhs: String) -> Bool { + public static func == (lhs: Path, rhs: String) -> Bool { lhs.value == rhs } } diff --git a/Sources/SimpleHTTP/Request/Request+URLRequest.swift b/Sources/SimpleHTTP/Request/Request+URLRequest.swift index f3c15ff..d033c9a 100644 --- a/Sources/SimpleHTTP/Request/Request+URLRequest.swift +++ b/Sources/SimpleHTTP/Request/Request+URLRequest.swift @@ -9,7 +9,11 @@ extension Request { /// - Parameter encoder: the encoder to use to encode the body is present /// - Parameter relativeTo: the base URL to append to the request path /// - Parameter accepting: if not nil will be used to set "Accept" header value - public func toURLRequest(encoder: ContentDataEncoder, relativeTo baseURL: URL, accepting: ContentDataDecoder? = nil) throws -> URLRequest { + public func toURLRequest( + encoder: ContentDataEncoder, + relativeTo baseURL: URL, + accepting: ContentDataDecoder? = nil + ) throws -> URLRequest { let request = try toURLRequest(encoder: encoder) .relativeTo(baseURL) diff --git a/Sources/SimpleHTTP/Response/URLSession+DataResponse.swift b/Sources/SimpleHTTP/Response/URLSession+DataResponse.swift index 6ffa7b5..7a760bf 100644 --- a/Sources/SimpleHTTP/Response/URLSession+DataResponse.swift +++ b/Sources/SimpleHTTP/Response/URLSession+DataResponse.swift @@ -8,6 +8,8 @@ extension URLSession { public func data(for urlRequest: URLRequest) async throws -> URLDataResponse { let (data, response) = try await data(for: urlRequest) + // swiftlint:disable force_cast return URLDataResponse(data: data, response: response as! HTTPURLResponse) + // swiftlint:enable force_cast } } diff --git a/Sources/SimpleHTTP/Session/SessionConfiguration.swift b/Sources/SimpleHTTP/Session/SessionConfiguration.swift index 6797548..f26e184 100644 --- a/Sources/SimpleHTTP/Session/SessionConfiguration.swift +++ b/Sources/SimpleHTTP/Session/SessionConfiguration.swift @@ -37,7 +37,7 @@ public struct SessionConfiguration { interceptors: CompositeInterceptor = [], dataError: DataError.Type ) { - self.init(encoder: encoder, decoder: decoder, decodingQueue: decodingQueue, interceptors: interceptors) + self.init(encoder: encoder, decoder: decoder, decodingQueue: decodingQueue, interceptors: interceptors) self.errorConverter = { try decoder.decode(dataError, from: $0) } diff --git a/Sources/SimpleHTTPFoundation/HTTP/HTTPHeader.swift b/Sources/SimpleHTTPFoundation/HTTP/HTTPHeader.swift index e548bd9..d931ad8 100644 --- a/Sources/SimpleHTTPFoundation/HTTP/HTTPHeader.swift +++ b/Sources/SimpleHTTPFoundation/HTTP/HTTPHeader.swift @@ -20,7 +20,9 @@ extension HTTPHeader { public static var contentDisposition: Self = "Content-Disposition" } +// swiftlint:disable line_length @available(*, unavailable, message: "This is a reserved header. See https://developer.apple.com/documentation/foundation/nsurlrequest#1776617") +// swiftlint:enable line_length extension HTTPHeader { public static let connection: Self = "Connection" public static let contentLength: Self = "Content-Length" diff --git a/Tests/.swiftlint.yml b/Tests/.swiftlint.yml new file mode 100644 index 0000000..c6673b2 --- /dev/null +++ b/Tests/.swiftlint.yml @@ -0,0 +1,4 @@ +disabled_rules: + - force_try + - force_cast + - xctfail_message diff --git a/Tests/SimpleHTTPTests/HTTPURLResponse+Fixture.swift b/Tests/SimpleHTTPTests/HTTPURLResponse+Fixture.swift index effdf4a..9bf458c 100644 --- a/Tests/SimpleHTTPTests/HTTPURLResponse+Fixture.swift +++ b/Tests/SimpleHTTPTests/HTTPURLResponse+Fixture.swift @@ -5,13 +5,13 @@ import FoundationNetworking #endif extension HTTPURLResponse { - convenience init(statusCode: Int) { - self.init(url: URL(string: "/")!, statusCode: statusCode, httpVersion: nil, headerFields: nil)! - } + convenience init(statusCode: Int) { + self.init(url: URL(string: "/")!, statusCode: statusCode, httpVersion: nil, headerFields: nil)! + } } extension URLResponse { - static let success = HTTPURLResponse(statusCode: 200) - static let unauthorized = HTTPURLResponse(statusCode: 401) - static let notFound = HTTPURLResponse(statusCode: 404) + static let success = HTTPURLResponse(statusCode: 200) + static let unauthorized = HTTPURLResponse(statusCode: 401) + static let notFound = HTTPURLResponse(statusCode: 404) } diff --git a/Tests/SimpleHTTPTests/MultipartForm/MultipartFormDataTest.swift b/Tests/SimpleHTTPTests/MultipartForm/MultipartFormDataTest.swift index 930995e..75c4446 100644 --- a/Tests/SimpleHTTPTests/MultipartForm/MultipartFormDataTest.swift +++ b/Tests/SimpleHTTPTests/MultipartForm/MultipartFormDataTest.swift @@ -3,66 +3,66 @@ import XCTest class MultipartFormDataTest: XCTestCase { - let crlf = EncodingCharacters.crlf + let crlf = EncodingCharacters.crlf - func test_addData_withoutFileNameAndMimeType_expectOneBodyPart() throws { - let boundary = "boundary" - var multipart = MultipartFormData(boundary: boundary) - let data = "I'm pjechris, Nice to meet you" - let name = "data" - let expectedHeaders: [Header] = [ - Header(name: .contentDisposition, value: "form-data; name=\"\(name)\"") - ] + func test_addData_withoutFileNameAndMimeType_expectOneBodyPart() throws { + let boundary = "boundary" + var multipart = MultipartFormData(boundary: boundary) + let data = "I'm pjechris, Nice to meet you" + let name = "data" + let expectedHeaders: [Header] = [ + Header(name: .contentDisposition, value: "form-data; name=\"\(name)\"") + ] - multipart.add(data: Data(data.utf8), name: name) + multipart.add(data: Data(data.utf8), name: name) - XCTAssertEqual(multipart.bodyParts.count, 1) - let bodyPart = try XCTUnwrap(multipart.bodyParts.first) - XCTAssertEqual(bodyPart.headers, expectedHeaders) - } + XCTAssertEqual(multipart.bodyParts.count, 1) + let bodyPart = try XCTUnwrap(multipart.bodyParts.first) + XCTAssertEqual(bodyPart.headers, expectedHeaders) + } - func test_addData_oneWithoutFileNameAndMimeType_secondWithAllValue_expect2BodyParts() throws { - let boundary = "boundary" - var multipart = MultipartFormData(boundary: boundary) - let data1 = "Swift" - let name1 = "swift" - let data2 = "Combine" - let name2 = "combine" - let fileName2 = "combine.txt" - let mimeType2 = "text/plain" - let expectedFirstBodyPartHeaders: [Header] = [ - Header(name: .contentDisposition, value: "form-data; name=\"\(name1)\"") - ] - let expectedLastBodyPartHeaders: [Header] = [ - Header(name: .contentDisposition, value: "form-data; name=\"\(name2)\"; filename=\"\(fileName2)\""), - Header(name: .contentType, value: mimeType2) - ] + func test_addData_oneWithoutFileNameAndMimeType_secondWithAllValue_expect2BodyParts() throws { + let boundary = "boundary" + var multipart = MultipartFormData(boundary: boundary) + let data1 = "Swift" + let name1 = "swift" + let data2 = "Combine" + let name2 = "combine" + let fileName2 = "combine.txt" + let mimeType2 = "text/plain" + let expectedFirstBodyPartHeaders: [Header] = [ + Header(name: .contentDisposition, value: "form-data; name=\"\(name1)\"") + ] + let expectedLastBodyPartHeaders: [Header] = [ + Header(name: .contentDisposition, value: "form-data; name=\"\(name2)\"; filename=\"\(fileName2)\""), + Header(name: .contentType, value: mimeType2) + ] - multipart.add(data: Data(data1.utf8), name: name1) - multipart.add(data: Data(data2.utf8), name: name2, fileName: fileName2, mimeType: mimeType2) + multipart.add(data: Data(data1.utf8), name: name1) + multipart.add(data: Data(data2.utf8), name: name2, fileName: fileName2, mimeType: mimeType2) - XCTAssertEqual(multipart.bodyParts.count, 2) - let bodyPart1 = try XCTUnwrap(multipart.bodyParts.first) - XCTAssertEqual(bodyPart1.headers, expectedFirstBodyPartHeaders) - let bodyPart2 = try XCTUnwrap(multipart.bodyParts.last) - XCTAssertEqual(bodyPart2.headers, expectedLastBodyPartHeaders) - } + XCTAssertEqual(multipart.bodyParts.count, 2) + let bodyPart1 = try XCTUnwrap(multipart.bodyParts.first) + XCTAssertEqual(bodyPart1.headers, expectedFirstBodyPartHeaders) + let bodyPart2 = try XCTUnwrap(multipart.bodyParts.last) + XCTAssertEqual(bodyPart2.headers, expectedLastBodyPartHeaders) + } - func test_addURL_bodyPart() throws { - let boundary = "boundary" - var multipart = MultipartFormData(boundary: boundary) - let url = URL.Images.swift - let name = "swift" - let expectedHeaders: [Header] = [ - Header(name: .contentDisposition, value: "form-data; name=\"\(name)\"; filename=\"swift.png\""), - Header(name: .contentType, value: "image/png") - ] + func test_addURL_bodyPart() throws { + let boundary = "boundary" + var multipart = MultipartFormData(boundary: boundary) + let url = URL.Images.swift + let name = "swift" + let expectedHeaders: [Header] = [ + Header(name: .contentDisposition, value: "form-data; name=\"\(name)\"; filename=\"swift.png\""), + Header(name: .contentType, value: "image/png") + ] - try multipart.add(url: url, name: name) + try multipart.add(url: url, name: name) - XCTAssertEqual(multipart.bodyParts.count, 1) - let bodyPart = try XCTUnwrap(multipart.bodyParts.first) - XCTAssertEqual(bodyPart.headers, expectedHeaders) - } + XCTAssertEqual(multipart.bodyParts.count, 1) + let bodyPart = try XCTUnwrap(multipart.bodyParts.first) + XCTAssertEqual(bodyPart.headers, expectedHeaders) + } } diff --git a/Tests/SimpleHTTPTests/Request/RequestTests.swift b/Tests/SimpleHTTPTests/Request/RequestTests.swift index 63730ba..66e5bdd 100644 --- a/Tests/SimpleHTTPTests/Request/RequestTests.swift +++ b/Tests/SimpleHTTPTests/Request/RequestTests.swift @@ -78,7 +78,10 @@ class RequestTests: XCTestCase { let request = try Request.post(.test, body: multipart) .toURLRequest(encoder: JSONEncoder(), relativeTo: baseURL) - XCTAssertEqual(request.allHTTPHeaderFields?["Content-Type"], HTTPContentType.multipart(boundary: multipart.boundary).value) + XCTAssertEqual( + request.allHTTPHeaderFields?["Content-Type"], + HTTPContentType.multipart(boundary: multipart.boundary).value + ) } } diff --git a/Tests/SimpleHTTPTests/URL+Fixture.swift b/Tests/SimpleHTTPTests/URL+Fixture.swift index c8c7a14..600082e 100644 --- a/Tests/SimpleHTTPTests/URL+Fixture.swift +++ b/Tests/SimpleHTTPTests/URL+Fixture.swift @@ -2,11 +2,9 @@ import Foundation extension URL { - enum Images { - - static let swift = URL.fromBundle(fileName: "swift", withExtension: "png")! - static let swiftUI = URL.fromBundle(fileName: "swiftUI", withExtension: "png")! - - } + enum Images { + static let swift = URL.fromBundle(fileName: "swift", withExtension: "png")! + static let swiftUI = URL.fromBundle(fileName: "swiftUI", withExtension: "png")! + } }