diff --git a/.github/dependabot.yml b/.github/dependabot.yml
index 7810eff..14c39b4 100644
--- a/.github/dependabot.yml
+++ b/.github/dependabot.yml
@@ -1,5 +1,4 @@
version: 2
-enable-beta-ecosystems: true
updates:
- package-ecosystem: "github-actions"
directory: "/"
@@ -11,14 +10,3 @@ updates:
dependencies:
patterns:
- "*"
- - package-ecosystem: "swift"
- directory: "/"
- schedule:
- interval: "daily"
- open-pull-requests-limit: 6
- allow:
- - dependency-type: all
- groups:
- all-dependencies:
- patterns:
- - "*"
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index dc2abf2..51c0262 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -5,4 +5,5 @@ on:
jobs:
unit-tests:
- uses: vapor/ci/.github/workflows/run-unit-tests.yml@reusable-workflows
+ uses: vapor/ci/.github/workflows/run-unit-tests.yml@main
+ secrets: inherit
diff --git a/Package@swift-5.9.swift b/Package@swift-5.9.swift
index 1cad414..365360c 100644
--- a/Package@swift-5.9.swift
+++ b/Package@swift-5.9.swift
@@ -25,6 +25,7 @@ let package = Package(
.product(name: "Collections", package: "swift-collections"),
],
swiftSettings: [
+ .enableUpcomingFeature("ExistentialAny"),
.enableExperimentalFeature("StrictConcurrency=complete"),
]
),
@@ -34,6 +35,7 @@ let package = Package(
.target(name: "MultipartKit"),
],
swiftSettings: [
+ .enableUpcomingFeature("ExistentialAny"),
.enableExperimentalFeature("StrictConcurrency=complete"),
]
),
diff --git a/README.md b/README.md
index 72fd4bd..3ab2ec9 100644
--- a/README.md
+++ b/README.md
@@ -9,7 +9,7 @@
-
+
@@ -18,18 +18,9 @@
### Installation
-The table below shows a list of MultipartKit major releases alongside their compatible NIO and Swift versions.
-
-|Version|NIO|Swift|SPM|
-|---|---|---|---|
-|4.0|2.2|5.4+|`from: "4.0.0"`|
-|3.0|1.0|4.0+|`from: "3.0.0"`|
-|2.0|N/A|3.1+|`from: "2.0.0"`|
-|1.0|N/A|3.1+|`from: "1.0.0"`|
-
Use the SPM string to easily include the dependency in your `Package.swift` file.
-Add MultiPartKit to your package dependencies:
+Add MultipartKit to your package dependencies:
```swift
dependencies: [
@@ -38,7 +29,7 @@ dependencies: [
]
```
-Add MultiPartKit to your target's dependencies:
+Add MultipartKit to your target's dependencies:
```swift
targets: [
diff --git a/Sources/MultipartKit/Docs.docc/index.md b/Sources/MultipartKit/Docs.docc/index.md
index fb613dd..88d1c8f 100644
--- a/Sources/MultipartKit/Docs.docc/index.md
+++ b/Sources/MultipartKit/Docs.docc/index.md
@@ -1,6 +1,8 @@
# ``MultipartKit``
-MultipartKit is a Swift package for parsing and serializing multipart/form-data requests. It provides hooks for encoding and decoding requests in Swift. It provides `Codable` support for the special case of the `multipart/form-data` media type through a ``FormDataEncoder`` and ``FormDataDecoder``. The parser delivers its output as it is parsed through callbacks suitable for streaming.
+Parser, serializer, and `Codable` support for `multipart/form-data`.
+
+MultipartKit is a Swift package for parsing and serializing `multipart/form-data` requests. It provides hooks for encoding and decoding requests in Swift and `Codable` support for handling `multipart/form-data` data through a ``FormDataEncoder`` and ``FormDataDecoder``. The parser delivers its output as it is parsed through callbacks suitable for streaming.
### Multipart Form Data
diff --git a/Sources/MultipartKit/Docs.docc/theme-settings.json b/Sources/MultipartKit/Docs.docc/theme-settings.json
index 09c9bb3..6e551b7 100644
--- a/Sources/MultipartKit/Docs.docc/theme-settings.json
+++ b/Sources/MultipartKit/Docs.docc/theme-settings.json
@@ -1,6 +1,6 @@
{
"theme": {
- "aside": { "border-radius": "6px", "border-style": "double", "border-width": "3px" },
+ "aside": { "border-radius": "16px", "border-style": "double", "border-width": "3px" },
"border-radius": "0",
"button": { "border-radius": "16px", "border-width": "1px", "border-style": "solid" },
"code": { "border-radius": "16px", "border-width": "1px", "border-style": "solid" },
diff --git a/Sources/MultipartKit/FormDataDecoder/FormDataDecoder.Decoder.swift b/Sources/MultipartKit/FormDataDecoder/FormDataDecoder.Decoder.swift
index d33d539..cbc3ae4 100644
--- a/Sources/MultipartKit/FormDataDecoder/FormDataDecoder.Decoder.swift
+++ b/Sources/MultipartKit/FormDataDecoder/FormDataDecoder.Decoder.swift
@@ -1,12 +1,12 @@
extension FormDataDecoder {
struct Decoder {
- let codingPath: [CodingKey]
+ let codingPath: [any CodingKey]
let data: MultipartFormData
let userInfo: [CodingUserInfoKey: Any]
- let previousCodingPath : [CodingKey]?
- let previousType: Decodable.Type?
+ let previousCodingPath: [any CodingKey]?
+ let previousType: (any Decodable.Type)?
- init(codingPath: [CodingKey], data: MultipartFormData, userInfo: [CodingUserInfoKey: Any], previousCodingPath: [CodingKey]? = nil, previousType: Decodable.Type? = nil) {
+ init(codingPath: [any CodingKey], data: MultipartFormData, userInfo: [CodingUserInfoKey: Any], previousCodingPath: [any CodingKey]? = nil, previousType: (any Decodable.Type)? = nil) {
self.codingPath = codingPath
self.data = data
self.userInfo = userInfo
@@ -24,26 +24,26 @@ extension FormDataDecoder.Decoder: Decoder {
return KeyedDecodingContainer(FormDataDecoder.KeyedContainer(data: dictionary, decoder: self))
}
- func unkeyedContainer() throws -> UnkeyedDecodingContainer {
+ func unkeyedContainer() throws -> any UnkeyedDecodingContainer {
guard let array = data.array else {
throw decodingError(expectedType: "array")
}
return FormDataDecoder.UnkeyedContainer(data: array, decoder: self)
}
- func singleValueContainer() throws -> SingleValueDecodingContainer {
+ func singleValueContainer() throws -> any SingleValueDecodingContainer {
self
}
}
extension FormDataDecoder.Decoder {
- func nested(at key: CodingKey, with data: MultipartFormData) -> Self {
+ func nested(at key: any CodingKey, with data: MultipartFormData) -> Self {
.init(codingPath: codingPath + [key], data: data, userInfo: userInfo)
}
}
private extension FormDataDecoder.Decoder {
- func decodingError(expectedType: String) -> Error {
+ func decodingError(expectedType: String) -> any Error {
let encounteredType: Any.Type
let encounteredTypeDescription: String
diff --git a/Sources/MultipartKit/FormDataDecoder/FormDataDecoder.KeyedContainer.swift b/Sources/MultipartKit/FormDataDecoder/FormDataDecoder.KeyedContainer.swift
index e9fb246..e523e23 100644
--- a/Sources/MultipartKit/FormDataDecoder/FormDataDecoder.KeyedContainer.swift
+++ b/Sources/MultipartKit/FormDataDecoder/FormDataDecoder.KeyedContainer.swift
@@ -10,7 +10,7 @@ extension FormDataDecoder.KeyedContainer: KeyedDecodingContainerProtocol {
data.keys.compactMap(K.init(stringValue:))
}
- var codingPath: [CodingKey] {
+ var codingPath: [any CodingKey] {
decoder.codingPath
}
@@ -18,7 +18,7 @@ extension FormDataDecoder.KeyedContainer: KeyedDecodingContainerProtocol {
data.keys.contains(key.stringValue)
}
- func getValue(forKey key: CodingKey) throws -> MultipartFormData {
+ func getValue(forKey key: any CodingKey) throws -> MultipartFormData {
guard let value = data[key.stringValue] else {
throw DecodingError.keyNotFound(
key, .init(
@@ -42,19 +42,19 @@ extension FormDataDecoder.KeyedContainer: KeyedDecodingContainerProtocol {
try decoderForKey(key).container(keyedBy: keyType)
}
- func nestedUnkeyedContainer(forKey key: K) throws -> UnkeyedDecodingContainer {
+ func nestedUnkeyedContainer(forKey key: K) throws -> any UnkeyedDecodingContainer {
try decoderForKey(key).unkeyedContainer()
}
- func superDecoder() throws -> Decoder {
+ func superDecoder() throws -> any Decoder {
try decoderForKey(BasicCodingKey.super)
}
- func superDecoder(forKey key: K) throws -> Decoder {
+ func superDecoder(forKey key: K) throws -> any Decoder {
try decoderForKey(key)
}
- func decoderForKey(_ key: CodingKey) throws -> FormDataDecoder.Decoder {
+ func decoderForKey(_ key: any CodingKey) throws -> FormDataDecoder.Decoder {
decoder.nested(at: key, with: try getValue(forKey: key))
}
}
diff --git a/Sources/MultipartKit/FormDataDecoder/FormDataDecoder.SingleValueContainer.swift b/Sources/MultipartKit/FormDataDecoder/FormDataDecoder.SingleValueContainer.swift
index 749e0a6..5108ce2 100644
--- a/Sources/MultipartKit/FormDataDecoder/FormDataDecoder.SingleValueContainer.swift
+++ b/Sources/MultipartKit/FormDataDecoder/FormDataDecoder.SingleValueContainer.swift
@@ -6,7 +6,7 @@ extension FormDataDecoder.Decoder: SingleValueDecodingContainer {
func decode(_: T.Type = T.self) throws -> T {
guard
let part = data.part,
- let Convertible = T.self as? MultipartPartConvertible.Type
+ let Convertible = T.self as? any MultipartPartConvertible.Type
else {
guard previousCodingPath?.count != codingPath.count || previousType != T.self else {
throw DecodingError.dataCorrupted(.init(codingPath: codingPath, debugDescription: "Decoding caught in recursion loop"))
diff --git a/Sources/MultipartKit/FormDataDecoder/FormDataDecoder.UnkeyedContainer.swift b/Sources/MultipartKit/FormDataDecoder/FormDataDecoder.UnkeyedContainer.swift
index 66294e4..d4779bd 100644
--- a/Sources/MultipartKit/FormDataDecoder/FormDataDecoder.UnkeyedContainer.swift
+++ b/Sources/MultipartKit/FormDataDecoder/FormDataDecoder.UnkeyedContainer.swift
@@ -7,11 +7,11 @@ extension FormDataDecoder {
}
extension FormDataDecoder.UnkeyedContainer: UnkeyedDecodingContainer {
- var codingPath: [CodingKey] {
+ var codingPath: [any CodingKey] {
decoder.codingPath
}
var count: Int? { data.count }
- var index: CodingKey { BasicCodingKey.index(currentIndex) }
+ var index: any CodingKey { BasicCodingKey.index(currentIndex) }
var isAtEnd: Bool { currentIndex >= data.count }
mutating func decodeNil() throws -> Bool {
@@ -26,11 +26,11 @@ extension FormDataDecoder.UnkeyedContainer: UnkeyedDecodingContainer {
try decoderAtIndex().container(keyedBy: keyType)
}
- mutating func nestedUnkeyedContainer() throws -> UnkeyedDecodingContainer {
+ mutating func nestedUnkeyedContainer() throws -> any UnkeyedDecodingContainer {
try decoderAtIndex().unkeyedContainer()
}
- mutating func superDecoder() throws -> Decoder {
+ mutating func superDecoder() throws -> any Decoder {
try decoderAtIndex()
}
diff --git a/Sources/MultipartKit/FormDataDecoder/FormDataDecoder.swift b/Sources/MultipartKit/FormDataDecoder/FormDataDecoder.swift
index 7e6f2c8..a793ed8 100644
--- a/Sources/MultipartKit/FormDataDecoder/FormDataDecoder.swift
+++ b/Sources/MultipartKit/FormDataDecoder/FormDataDecoder.swift
@@ -15,7 +15,7 @@ public struct FormDataDecoder: Sendable {
let nestingDepth: Int
/// Any contextual information set by the user for decoding.
- public var userInfo: [CodingUserInfoKey: Sendable] = [:]
+ public var userInfo: [CodingUserInfoKey: any Sendable] = [:]
/// Creates a new `FormDataDecoder`.
/// - Parameter nestingDepth: maximum allowed nesting depth of the decoded structure. Defaults to 8.
diff --git a/Sources/MultipartKit/FormDataEncoder/FormDataEncoder.Encoder.swift b/Sources/MultipartKit/FormDataEncoder/FormDataEncoder.Encoder.swift
index 6fbd11e..7e0a41c 100644
--- a/Sources/MultipartKit/FormDataEncoder/FormDataEncoder.Encoder.swift
+++ b/Sources/MultipartKit/FormDataEncoder/FormDataEncoder.Encoder.swift
@@ -1,6 +1,6 @@
extension FormDataEncoder {
struct Encoder {
- let codingPath: [CodingKey]
+ let codingPath: [any CodingKey]
let storage = Storage()
let userInfo: [CodingUserInfoKey: Any]
}
@@ -13,19 +13,19 @@ extension FormDataEncoder.Encoder: Encoder {
return .init(container)
}
- func unkeyedContainer() -> UnkeyedEncodingContainer {
+ func unkeyedContainer() -> any UnkeyedEncodingContainer {
let container = FormDataEncoder.UnkeyedContainer(encoder: self)
storage.dataContainer = container.dataContainer
return container
}
- func singleValueContainer() -> SingleValueEncodingContainer {
+ func singleValueContainer() -> any SingleValueEncodingContainer {
self
}
}
extension FormDataEncoder.Encoder {
- func nested(at key: CodingKey) -> FormDataEncoder.Encoder {
+ func nested(at key: any CodingKey) -> FormDataEncoder.Encoder {
.init(codingPath: codingPath + [key], userInfo: userInfo)
}
}
diff --git a/Sources/MultipartKit/FormDataEncoder/FormDataEncoder.KeyedContainer.swift b/Sources/MultipartKit/FormDataEncoder/FormDataEncoder.KeyedContainer.swift
index 6046d66..e72ec72 100644
--- a/Sources/MultipartKit/FormDataEncoder/FormDataEncoder.KeyedContainer.swift
+++ b/Sources/MultipartKit/FormDataEncoder/FormDataEncoder.KeyedContainer.swift
@@ -6,7 +6,7 @@ extension FormDataEncoder {
}
extension FormDataEncoder.KeyedContainer: KeyedEncodingContainerProtocol {
- var codingPath: [CodingKey] {
+ var codingPath: [any CodingKey] {
encoder.codingPath
}
@@ -22,19 +22,19 @@ extension FormDataEncoder.KeyedContainer: KeyedEncodingContainerProtocol {
encoderForKey(key).container(keyedBy: keyType)
}
- func nestedUnkeyedContainer(forKey key: Key) -> UnkeyedEncodingContainer {
+ func nestedUnkeyedContainer(forKey key: Key) -> any UnkeyedEncodingContainer {
encoderForKey(key).unkeyedContainer()
}
- func superEncoder() -> Encoder {
+ func superEncoder() -> any Encoder {
encoderForKey(BasicCodingKey.super)
}
- func superEncoder(forKey key: Key) -> Encoder {
+ func superEncoder(forKey key: Key) -> any Encoder {
encoderForKey(key)
}
- func encoderForKey(_ key: CodingKey) -> FormDataEncoder.Encoder {
+ func encoderForKey(_ key: any CodingKey) -> FormDataEncoder.Encoder {
let encoder = self.encoder.nested(at: key)
dataContainer.value[key.stringValue] = encoder.storage
return encoder
diff --git a/Sources/MultipartKit/FormDataEncoder/FormDataEncoder.SingleValueContainer.swift b/Sources/MultipartKit/FormDataEncoder/FormDataEncoder.SingleValueContainer.swift
index b905f22..d5c9f60 100644
--- a/Sources/MultipartKit/FormDataEncoder/FormDataEncoder.SingleValueContainer.swift
+++ b/Sources/MultipartKit/FormDataEncoder/FormDataEncoder.SingleValueContainer.swift
@@ -5,7 +5,7 @@ extension FormDataEncoder.Encoder: SingleValueEncodingContainer {
func encode(_ value: T) throws {
if
- let convertible = value as? MultipartPartConvertible,
+ let convertible = value as? any MultipartPartConvertible,
let part = convertible.multipart
{
storage.dataContainer = SingleValueDataContainer(part: part)
diff --git a/Sources/MultipartKit/FormDataEncoder/FormDataEncoder.UnkeyedContainer.swift b/Sources/MultipartKit/FormDataEncoder/FormDataEncoder.UnkeyedContainer.swift
index 35ca9ee..6344a2f 100644
--- a/Sources/MultipartKit/FormDataEncoder/FormDataEncoder.UnkeyedContainer.swift
+++ b/Sources/MultipartKit/FormDataEncoder/FormDataEncoder.UnkeyedContainer.swift
@@ -6,7 +6,7 @@ extension FormDataEncoder {
}
extension FormDataEncoder.UnkeyedContainer: UnkeyedEncodingContainer {
- var codingPath: [CodingKey] {
+ var codingPath: [any CodingKey] {
encoder.codingPath
}
@@ -26,11 +26,11 @@ extension FormDataEncoder.UnkeyedContainer: UnkeyedEncodingContainer {
nextEncoder().container(keyedBy: keyType)
}
- func nestedUnkeyedContainer() -> UnkeyedEncodingContainer {
+ func nestedUnkeyedContainer() -> any UnkeyedEncodingContainer {
nextEncoder().unkeyedContainer()
}
- func superEncoder() -> Encoder {
+ func superEncoder() -> any Encoder {
nextEncoder()
}
diff --git a/Sources/MultipartKit/FormDataEncoder/FormDataEncoder.swift b/Sources/MultipartKit/FormDataEncoder/FormDataEncoder.swift
index f48066d..2579ff7 100644
--- a/Sources/MultipartKit/FormDataEncoder/FormDataEncoder.swift
+++ b/Sources/MultipartKit/FormDataEncoder/FormDataEncoder.swift
@@ -8,7 +8,7 @@ import NIOCore
public struct FormDataEncoder: Sendable {
/// Any contextual information set by the user for encoding.
- public var userInfo: [CodingUserInfoKey: Sendable] = [:]
+ public var userInfo: [CodingUserInfoKey: any Sendable] = [:]
/// Creates a new `FormDataEncoder`.
public init() { }
diff --git a/Sources/MultipartKit/FormDataEncoder/Storage.swift b/Sources/MultipartKit/FormDataEncoder/Storage.swift
index 23a081b..7a7c01d 100644
--- a/Sources/MultipartKit/FormDataEncoder/Storage.swift
+++ b/Sources/MultipartKit/FormDataEncoder/Storage.swift
@@ -1,7 +1,7 @@
import Collections
final class Storage {
- var dataContainer: DataContainer? = nil
+ var dataContainer: (any DataContainer)? = nil
var data: MultipartFormData? {
dataContainer?.data
}
diff --git a/Sources/MultipartKit/MultipartPart.swift b/Sources/MultipartKit/MultipartPart.swift
index 2281fc1..8e3a709 100644
--- a/Sources/MultipartKit/MultipartPart.swift
+++ b/Sources/MultipartKit/MultipartPart.swift
@@ -53,16 +53,11 @@ public struct MultipartPart: Equatable, Sendable {
extension Array where Element == MultipartPart {
/// Returns the first `MultipartPart` with matching name attribute in `"Content-Disposition"` header.
public func firstPart(named name: String) -> MultipartPart? {
- for el in self {
- if el.name == name {
- return el
- }
- }
- return nil
+ self.first { $0.name == name }
}
/// Returns all `MultipartPart`s with matching name attribute in `"Content-Disposition"` header.
public func allParts(named name: String) -> [MultipartPart] {
- filter { $0.name == name }
+ self.filter { $0.name == name }
}
}
diff --git a/Sources/MultipartKit/MultipartPartConvertible.swift b/Sources/MultipartKit/MultipartPartConvertible.swift
index ae070b7..5a637c1 100644
--- a/Sources/MultipartKit/MultipartPartConvertible.swift
+++ b/Sources/MultipartKit/MultipartPartConvertible.swift
@@ -1,14 +1,18 @@
import struct Foundation.Data
-import struct Foundation.UUID
+import struct Foundation.URL
+/// A protocol to provide custom behaviors for parsing and serializing types from and to multipart data.
public protocol MultipartPartConvertible {
var multipart: MultipartPart? { get }
+
init?(multipart: MultipartPart)
}
+// MARK: MultipartPart self-conformance
+
extension MultipartPart: MultipartPartConvertible {
public var multipart: MultipartPart? {
- return self
+ self
}
public init?(multipart: MultipartPart) {
@@ -16,9 +20,11 @@ extension MultipartPart: MultipartPartConvertible {
}
}
+// MARK: String
+
extension String: MultipartPartConvertible {
public var multipart: MultipartPart? {
- return MultipartPart(body: self)
+ .init(body: self)
}
public init?(multipart: MultipartPart) {
@@ -26,16 +32,15 @@ extension String: MultipartPartConvertible {
}
}
+// MARK: Numbers
+
extension FixedWidthInteger {
public var multipart: MultipartPart? {
- return MultipartPart(body: self.description)
+ .init(body: self.description)
}
public init?(multipart: MultipartPart) {
- guard let string = String(multipart: multipart) else {
- return nil
- }
- self.init(string)
+ self.init(String(multipart: multipart)!) // String.init(multipart:) never returns nil
}
}
@@ -52,49 +57,54 @@ extension UInt64: MultipartPartConvertible { }
extension Float: MultipartPartConvertible {
public var multipart: MultipartPart? {
- return MultipartPart(body: self.description)
+ .init(body: self.description)
}
public init?(multipart: MultipartPart) {
- guard let string = String(multipart: multipart) else {
- return nil
- }
- self.init(string)
+ self.init(String(multipart: multipart)!) // String.init(multipart:) never returns nil
}
}
extension Double: MultipartPartConvertible {
public var multipart: MultipartPart? {
- return MultipartPart(body: self.description)
+ .init(body: self.description)
}
public init?(multipart: MultipartPart) {
- guard let string = String(multipart: multipart) else {
- return nil
- }
- self.init(string)
+ self.init(String(multipart: multipart)!) // String.init(multipart:) never returns nil
}
}
+// MARK: Bool
+
extension Bool: MultipartPartConvertible {
public var multipart: MultipartPart? {
- return MultipartPart(body: self.description)
+ .init(body: self.description)
}
public init?(multipart: MultipartPart) {
- guard let string = String(multipart: multipart) else {
- return nil
- }
- self.init(string)
+ self.init(String(multipart: multipart)!) // String.init(multipart:) never returns nil
}
}
+// MARK: Foundation types
+
extension Data: MultipartPartConvertible {
public var multipart: MultipartPart? {
- return MultipartPart(body: self)
+ .init(body: self)
}
public init?(multipart: MultipartPart) {
self.init(multipart.body.readableBytesView)
}
}
+
+extension URL: MultipartPartConvertible {
+ public var multipart: MultipartPart? {
+ .init(body: self.absoluteString)
+ }
+
+ public init?(multipart: MultipartPart) {
+ self.init(string: String(multipart: multipart)!) // String.init(multipart:) never returns nil
+ }
+}
diff --git a/Sources/MultipartKit/Utilities.swift b/Sources/MultipartKit/Utilities.swift
index fbaf0be..3586f5d 100644
--- a/Sources/MultipartKit/Utilities.swift
+++ b/Sources/MultipartKit/Utilities.swift
@@ -19,16 +19,19 @@ extension HTTPHeaders {
defaultValue: String
) {
var current: [String]
+
if let existing = self.headerParts(name: name) {
current = existing.filter { !$0.hasPrefix("\(key)=") }
} else {
current = [defaultValue]
}
+
if let value = value {
current.append("\(key)=\"\(value)\"")
}
- let new = current.joined(separator: "; ")
- .trimmingCharacters(in: .whitespaces)
+
+ let new = current.joined(separator: "; ").trimmingCharacters(in: .whitespaces)
+
self.replaceOrAdd(name: name, value: new)
}
diff --git a/Tests/MultipartKitTests/FormDataTests.swift b/Tests/MultipartKitTests/FormDataTests.swift
index 20d6d56..3199f55 100644
--- a/Tests/MultipartKitTests/FormDataTests.swift
+++ b/Tests/MultipartKitTests/FormDataTests.swift
@@ -1,7 +1,7 @@
import XCTest
import MultipartKit
-class FormDataTests: XCTestCase {
+final class FormDataTests: XCTestCase {
func testFormDataEncoder() throws {
struct Foo: Encodable {
var string: String
@@ -159,7 +159,11 @@ class FormDataTests: XCTestCase {
"""
struct Foo: Decodable {
- var link: URL
+ struct Bar: Decodable {
+ var relative: String
+ var base: String?
+ }
+ var link: Bar
}
XCTAssertThrowsError(try FormDataDecoder().decode(Foo.self, from: data, boundary: "hello")) { error in
@@ -391,7 +395,7 @@ class FormDataTests: XCTestCase {
let success: Bool
- init(from _: Decoder) throws {
+ init(from _: any Decoder) throws {
success = false
}
init?(multipart: MultipartPart) {
@@ -507,4 +511,46 @@ class FormDataTests: XCTestCase {
"""
XCTAssertThrowsError (try FormDataDecoder().decode(TestData.self, from: multipart, boundary: "-"))
}
+
+ func testCodingDataTypes() throws {
+ struct AllTypes: Codable, Equatable {
+ let string: String
+ let int: Int, int8: Int8, int16: Int16, int32: Int32, int64: Int64
+ let uint: UInt, uint8: UInt8, uint16: UInt16, uint32: UInt32, uint64: UInt64
+ let float: Float, double: Double
+ let bool: Bool
+ let data: Data, url: URL
+ }
+ let value = AllTypes(
+ string: "string",
+ int: 1, int8: 2, int16: 3, int32: 4, int64: 5,
+ uint: 6, uint8: 7, uint16: 8, uint32: 9, uint64: 0,
+ float: 1.0, double: -1.0,
+ bool: false,
+ data: .init([.init(ascii: "A")]), url: .init(string: "https://apple.com/")!
+ )
+ let multipart = """
+ ---\r\nContent-Disposition: form-data; name="string"\r\n\r\nstring\r
+ ---\r\nContent-Disposition: form-data; name="int"\r\n\r\n1\r
+ ---\r\nContent-Disposition: form-data; name="int8"\r\n\r\n2\r
+ ---\r\nContent-Disposition: form-data; name="int16"\r\n\r\n3\r
+ ---\r\nContent-Disposition: form-data; name="int32"\r\n\r\n4\r
+ ---\r\nContent-Disposition: form-data; name="int64"\r\n\r\n5\r
+ ---\r\nContent-Disposition: form-data; name="uint"\r\n\r\n6\r
+ ---\r\nContent-Disposition: form-data; name="uint8"\r\n\r\n7\r
+ ---\r\nContent-Disposition: form-data; name="uint16"\r\n\r\n8\r
+ ---\r\nContent-Disposition: form-data; name="uint32"\r\n\r\n9\r
+ ---\r\nContent-Disposition: form-data; name="uint64"\r\n\r\n0\r
+ ---\r\nContent-Disposition: form-data; name="float"\r\n\r\n1.0\r
+ ---\r\nContent-Disposition: form-data; name="double"\r\n\r\n-1.0\r
+ ---\r\nContent-Disposition: form-data; name="bool"\r\n\r\nfalse\r
+ ---\r\nContent-Disposition: form-data; name="data"\r\n\r\nA\r
+ ---\r\nContent-Disposition: form-data; name="url"\r\n\r\nhttps://apple.com/\r
+ -----\r\n
+ """
+
+ XCTAssertEqual(try FormDataEncoder().encode(value, boundary: "-"), multipart)
+ XCTAssertEqual(try FormDataDecoder().decode(AllTypes.self, from: multipart, boundary: "-"), value)
+
+ }
}
diff --git a/Tests/MultipartKitTests/MultipartTests.swift b/Tests/MultipartKitTests/MultipartTests.swift
index 6101999..af08574 100644
--- a/Tests/MultipartKitTests/MultipartTests.swift
+++ b/Tests/MultipartKitTests/MultipartTests.swift
@@ -3,7 +3,7 @@ import MultipartKit
import NIOCore
import NIOHTTP1
-class MultipartTests: XCTestCase {
+final class MultipartTests: XCTestCase {
let named = """
test123
aijdisadi>SDASD