Skip to content

Commit 8927764

Browse files
Add support for array indices in query params decoding (#542)
* Add support for array indices in query params decoding * Resolve PR comments
1 parent b286e64 commit 8927764

File tree

3 files changed

+59
-5
lines changed

3 files changed

+59
-5
lines changed

Sources/Hummingbird/Codable/URLEncodedForm/URLEncodedFormNode.swift

+16-4
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,10 @@ enum URLEncodedFormNode: CustomStringConvertible, Equatable {
77
/// holds an array of nodes
88
case array(Array)
99

10-
enum Error: Swift.Error {
10+
enum Error: Swift.Error, Equatable {
1111
case failedToDecode(String? = nil)
1212
case notSupported
13+
case invalidArrayIndex(Int)
1314
}
1415

1516
/// Initialize node from URL encoded form data
@@ -51,7 +52,7 @@ enum URLEncodedFormNode: CustomStringConvertible, Equatable {
5152
/// function for create `URLEncodedFormNode` from `KeyParser.Key.Type`
5253
func createNode(from key: KeyParser.KeyType) -> URLEncodedFormNode {
5354
switch key {
54-
case .array:
55+
case .array, .arrayWithIndices:
5556
return .array(.init())
5657
case .map:
5758
return .map(.init())
@@ -84,6 +85,11 @@ enum URLEncodedFormNode: CustomStringConvertible, Equatable {
8485
// currently don't support arrays and maps inside arrays
8586
throw Error.notSupported
8687
}
88+
case (.array(let array), .arrayWithIndices(let index)):
89+
guard keys.count == 0, array.values.count == index else {
90+
throw Error.invalidArrayIndex(index)
91+
}
92+
array.values.append(.leaf(value))
8793
default:
8894
throw Error.failedToDecode()
8995
}
@@ -168,7 +174,7 @@ enum URLEncodedFormNode: CustomStringConvertible, Equatable {
168174

169175
/// Parse URL encoded key
170176
enum KeyParser {
171-
enum KeyType: Equatable { case map(Substring), array }
177+
enum KeyType: Equatable { case map(Substring), array, arrayWithIndices(Int) }
172178

173179
static func parse(_ key: String) -> [KeyType]? {
174180
var index = key.startIndex
@@ -186,13 +192,19 @@ enum KeyParser {
186192
index = key.index(after: index)
187193
// an open bracket is unexpected
188194
guard index != key.endIndex else { return nil }
195+
189196
if key[index] == "]" {
190197
values.append(.array)
191198
index = key.index(after: index)
192199
} else {
193200
// an open bracket is unexpected
194201
guard let bracketIndex = key[index...].firstIndex(of: "]") else { return nil }
195-
values.append(.map(key[index..<bracketIndex]))
202+
// If key can convert to an integer assume it is an array index
203+
if let index = Int(key[index..<bracketIndex]) {
204+
values.append(.arrayWithIndices(index))
205+
} else {
206+
values.append(.map(key[index..<bracketIndex]))
207+
}
196208
index = bracketIndex
197209
index = key.index(after: index)
198210
}

Tests/HummingbirdTests/URLEncodedForm/URLDecoderTests.swift

+38-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
//
1313
//===----------------------------------------------------------------------===//
1414

15-
import Hummingbird
15+
@testable import Hummingbird
1616
import XCTest
1717

1818
class URLDecodedFormDecoderTests: XCTestCase {
@@ -79,6 +79,43 @@ class URLDecodedFormDecoderTests: XCTestCase {
7979
self.testForm(test, query: "b[]=true&b[]=false&i[]=34&i8[]=23&i16[]=9&i32[]=-6872&i64[]=23&u[]=0&u8[]=255&u16[]=7673&u32[]=88222&u64[]=234&f[]=-1.1&d[]=8")
8080
}
8181

82+
func testArraysWithIndices() {
83+
struct Test: Codable, Equatable {
84+
let arr: [Int]
85+
}
86+
let test = Test(arr: [12, 45, 54, 55, -5, 5])
87+
self.testForm(test, query: "arr[0]=12&arr[1]=45&arr[2]=54&arr[3]=55&arr[4]=-5&arr[5]=5")
88+
89+
let test2 = Test(arr: [12, 45, 54, 55, -5, 5, 9, 33, 0, 9, 4, 33])
90+
let query = """
91+
arr[0]=12\
92+
&arr[1]=45\
93+
&arr[2]=54\
94+
&arr[3]=55\
95+
&arr[4]=-5\
96+
&arr[5]=5\
97+
&arr[6]=9\
98+
&arr[7]=33\
99+
&arr[8]=0\
100+
&arr[9]=9\
101+
&arr[10]=4\
102+
&arr[11]=33
103+
"""
104+
self.testForm(test2, query: query)
105+
}
106+
107+
func testArrayWithIndicesThrows() {
108+
struct Test: Codable, Equatable {
109+
let arr: [Int]
110+
}
111+
let decoder = URLEncodedFormDecoder()
112+
// incorrect indices
113+
let query = "arr[0]=2&arr[2]=4"
114+
XCTAssertThrowsError(try decoder.decode(Test.self, from: query)) { error in
115+
XCTAssertEqual(error as? URLEncodedFormNode.Error, URLEncodedFormNode.Error.invalidArrayIndex(2))
116+
}
117+
}
118+
82119
func testStringSpecialCharactersDecode() {
83120
struct Test: Codable, Equatable {
84121
let a: String

Tests/HummingbirdTests/URLEncodedForm/URLEncodedNodeTests.swift

+5
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,11 @@ class URLEncodedFormNodeTests: XCTestCase {
4646
XCTAssertEqual(values, [.map("array"), .array])
4747
}
4848

49+
func testKeyParserArrayWithIndices() {
50+
let values = KeyParser.parse("array[0]")
51+
XCTAssertEqual(values, [.map("array"), .arrayWithIndices(0)])
52+
}
53+
4954
func testKeyParserMap() {
5055
let values = KeyParser.parse("array[object]")
5156
XCTAssertEqual(values, [.map("array"), .map("object")])

0 commit comments

Comments
 (0)