From e9a99c2fab68a0361cf384017844d5aaec23a7aa Mon Sep 17 00:00:00 2001
From: Siemen Sikkema
Date: Wed, 29 Sep 2021 13:55:02 +0200
Subject: [PATCH] Group Tests into pure Multipart & Form Data (#72)
---
NOTICE.txt | 2 +-
README.md | 114 +++++++
...partKitTests.swift => FormDataTests.swift} | 297 +-----------------
Tests/MultipartKitTests/MultipartTests.swift | 294 +++++++++++++++++
4 files changed, 410 insertions(+), 297 deletions(-)
rename Tests/MultipartKitTests/{MultipartKitTests.swift => FormDataTests.swift} (61%)
create mode 100644 Tests/MultipartKitTests/MultipartTests.swift
diff --git a/NOTICE.txt b/NOTICE.txt
index 4fc2a02..1a9a459 100644
--- a/NOTICE.txt
+++ b/NOTICE.txt
@@ -17,4 +17,4 @@ Swift Collections.
* LICENSE (Apache License 2.0):
* https://swift.org/LICENSE.txt
* HOMEPAGE:
- * https://github.com/apple/swift-collections
\ No newline at end of file
+ * https://github.com/apple/swift-collections
diff --git a/README.md b/README.md
index 69f66ab..ed1dda2 100644
--- a/README.md
+++ b/README.md
@@ -21,3 +21,117 @@
+
+🏞 Multipart parser and serializer with `Codable` support for Multipart Form Data.
+
+### Major Releases
+
+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.2+|`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.
+
+```swift
+.package(url: "https://github.com/vapor/multipart-kit.git", from: ...)
+```
+
+### Supported Platforms
+
+MultipartKit supports the following platforms:
+
+- Ubuntu 18.04+
+- macOS 10.15+
+
+## Overview
+
+MultipartKit is a multipart parsing and serializing library. 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.
+
+### Multipart Form Data
+
+Let's define a `Codable` type and a choose a boundary used to separate the multipart parts.
+
+```swift
+struct User: Codable {
+ let name: String
+ let email: String
+}
+let user = User(name: "Ed", email: "ed@example.com")
+let boundary = "abc123"
+```
+
+We can encode this instance of a our type using a `FormDataEncoder`.
+
+```swift
+let encoded = try FormDataEncoder().encode(foo, boundary: boundary)
+```
+
+The output looks then looks like this.
+```
+--abc123
+Content-Disposition: form-data; name="name"
+
+Ed
+--abc123
+Content-Disposition: form-data; name="email"
+
+ed@example.com
+--abc123--
+```
+
+In order to _decode_ this message we feed this output and the same boundary to a `FormDataDecoder` and we get back an identical instance to the one we started with.
+
+```swift
+let decoded = try FormDataDecoder().decode(User.self, from: encoded, boundary: boundary)
+```
+
+### A note on `null`
+As there is no standard defined for how to represent `null` in Multipart (unlike, for instance, JSON), FormDataEncoder and FormDataDecoder do not support encoding or decoding `null` respectively.
+
+### Nesting and Collections
+
+Nested structures can be represented by naming the parts such that they describe a path using square brackets to denote contained properties or elements in a collection. The following example shows what that looks like in practice.
+
+```swift
+struct Nested: Encodable {
+ let tag: String
+ let flag: Bool
+ let nested: [Nested]
+}
+let boundary = "abc123"
+let nested = Nested(tag: "a", flag: true, nested: [Nested(tag: "b", flag: false, nested: [])])
+let encoded = try FormDataEncoder().encode(nested, boundary: boundary)
+```
+
+This results in the content below.
+
+```
+--abc123
+Content-Disposition: form-data; name="tag"
+
+a
+--abc123
+Content-Disposition: form-data; name="flag"
+
+true
+--abc123
+Content-Disposition: form-data; name="nested[0][tag]"
+
+b
+--abc123
+Content-Disposition: form-data; name="nested[0][flag]"
+
+false
+--abc123--
+```
+
+Note that the array elements always include the index (as opposed to just `[]`) in order to support complex nesting.
+
+### Attribution
+
+This library contains code from the `OrderedCollection` module from https://github.com/apple/swift-collections. See: NOTICE.txt.
\ No newline at end of file
diff --git a/Tests/MultipartKitTests/MultipartKitTests.swift b/Tests/MultipartKitTests/FormDataTests.swift
similarity index 61%
rename from Tests/MultipartKitTests/MultipartKitTests.swift
rename to Tests/MultipartKitTests/FormDataTests.swift
index 70a600c..d9c144a 100644
--- a/Tests/MultipartKitTests/MultipartKitTests.swift
+++ b/Tests/MultipartKitTests/FormDataTests.swift
@@ -1,97 +1,7 @@
import XCTest
import MultipartKit
-class MultipartTests: XCTestCase {
- let named = """
- test123
- aijdisadi>SDASDdwekqie4u219034u129e0wque90qjsd90asffs
-
-
- SDASD [SubSequence] {
- precondition(maxLength > 0, "groups must be greater than zero")
- var start = startIndex
- return stride(from: 0, to: count, by: maxLength).map { _ in
- let end = index(start, offsetBy: maxLength, limitedBy: endIndex) ?? endIndex
- defer { start = end }
- return self[start.. MultipartParserOutputReceiver {
- try collectOutput(ByteBuffer(string: data), boundary: boundary)
- }
-
- static func collectOutput(_ data: ByteBuffer, boundary: String) throws -> MultipartParserOutputReceiver {
- let output = MultipartParserOutputReceiver()
- let parser = MultipartParser(boundary: boundary)
- output.setUp(with: parser)
- try parser.execute(data)
- return output
- }
-
- func setUp(with parser: MultipartParser) {
- parser.onHeader = { (field, value) in
- self.headers.replaceOrAdd(name: field, value: value)
- }
- parser.onBody = { new in
- self.body.writeBuffer(&new)
- }
- parser.onPartComplete = {
- let part = MultipartPart(headers: self.headers, body: self.body)
- self.headers = [:]
- self.body = ByteBuffer()
- self.parts.append(part)
- }
- }
-}
diff --git a/Tests/MultipartKitTests/MultipartTests.swift b/Tests/MultipartKitTests/MultipartTests.swift
new file mode 100644
index 0000000..8ec18f2
--- /dev/null
+++ b/Tests/MultipartKitTests/MultipartTests.swift
@@ -0,0 +1,294 @@
+import XCTest
+import MultipartKit
+
+class MultipartTests: XCTestCase {
+ let named = """
+ test123
+ aijdisadi>SDASDdwekqie4u219034u129e0wque90qjsd90asffs
+
+
+ SDASD [SubSequence] {
+ precondition(maxLength > 0, "groups must be greater than zero")
+ var start = startIndex
+ return stride(from: 0, to: count, by: maxLength).map { _ in
+ let end = index(start, offsetBy: maxLength, limitedBy: endIndex) ?? endIndex
+ defer { start = end }
+ return self[start.. MultipartParserOutputReceiver {
+ try collectOutput(ByteBuffer(string: data), boundary: boundary)
+ }
+
+ static func collectOutput(_ data: ByteBuffer, boundary: String) throws -> MultipartParserOutputReceiver {
+ let output = MultipartParserOutputReceiver()
+ let parser = MultipartParser(boundary: boundary)
+ output.setUp(with: parser)
+ try parser.execute(data)
+ return output
+ }
+
+ func setUp(with parser: MultipartParser) {
+ parser.onHeader = { (field, value) in
+ self.headers.replaceOrAdd(name: field, value: value)
+ }
+ parser.onBody = { new in
+ self.body.writeBuffer(&new)
+ }
+ parser.onPartComplete = {
+ let part = MultipartPart(headers: self.headers, body: self.body)
+ self.headers = [:]
+ self.body = ByteBuffer()
+ self.parts.append(part)
+ }
+ }
+}