Skip to content

Commit

Permalink
Add support for array indices in query params decoding (#542)
Browse files Browse the repository at this point in the history
* Add support for array indices in query params decoding

* Resolve PR comments
  • Loading branch information
supersonicbyte committed Sep 9, 2024
1 parent b286e64 commit 8927764
Show file tree
Hide file tree
Showing 3 changed files with 59 additions and 5 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,10 @@ enum URLEncodedFormNode: CustomStringConvertible, Equatable {
/// holds an array of nodes
case array(Array)

enum Error: Swift.Error {
enum Error: Swift.Error, Equatable {
case failedToDecode(String? = nil)
case notSupported
case invalidArrayIndex(Int)
}

/// Initialize node from URL encoded form data
Expand Down Expand Up @@ -51,7 +52,7 @@ enum URLEncodedFormNode: CustomStringConvertible, Equatable {
/// function for create `URLEncodedFormNode` from `KeyParser.Key.Type`
func createNode(from key: KeyParser.KeyType) -> URLEncodedFormNode {
switch key {
case .array:
case .array, .arrayWithIndices:
return .array(.init())
case .map:
return .map(.init())
Expand Down Expand Up @@ -84,6 +85,11 @@ enum URLEncodedFormNode: CustomStringConvertible, Equatable {
// currently don't support arrays and maps inside arrays
throw Error.notSupported
}
case (.array(let array), .arrayWithIndices(let index)):
guard keys.count == 0, array.values.count == index else {
throw Error.invalidArrayIndex(index)
}
array.values.append(.leaf(value))
default:
throw Error.failedToDecode()
}
Expand Down Expand Up @@ -168,7 +174,7 @@ enum URLEncodedFormNode: CustomStringConvertible, Equatable {

/// Parse URL encoded key
enum KeyParser {
enum KeyType: Equatable { case map(Substring), array }
enum KeyType: Equatable { case map(Substring), array, arrayWithIndices(Int) }

static func parse(_ key: String) -> [KeyType]? {
var index = key.startIndex
Expand All @@ -186,13 +192,19 @@ enum KeyParser {
index = key.index(after: index)
// an open bracket is unexpected
guard index != key.endIndex else { return nil }

if key[index] == "]" {
values.append(.array)
index = key.index(after: index)
} else {
// an open bracket is unexpected
guard let bracketIndex = key[index...].firstIndex(of: "]") else { return nil }
values.append(.map(key[index..<bracketIndex]))
// If key can convert to an integer assume it is an array index
if let index = Int(key[index..<bracketIndex]) {
values.append(.arrayWithIndices(index))
} else {
values.append(.map(key[index..<bracketIndex]))
}
index = bracketIndex
index = key.index(after: index)
}
Expand Down
39 changes: 38 additions & 1 deletion Tests/HummingbirdTests/URLEncodedForm/URLDecoderTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
//
//===----------------------------------------------------------------------===//

import Hummingbird
@testable import Hummingbird
import XCTest

class URLDecodedFormDecoderTests: XCTestCase {
Expand Down Expand Up @@ -79,6 +79,43 @@ class URLDecodedFormDecoderTests: XCTestCase {
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")
}

func testArraysWithIndices() {
struct Test: Codable, Equatable {
let arr: [Int]
}
let test = Test(arr: [12, 45, 54, 55, -5, 5])
self.testForm(test, query: "arr[0]=12&arr[1]=45&arr[2]=54&arr[3]=55&arr[4]=-5&arr[5]=5")

let test2 = Test(arr: [12, 45, 54, 55, -5, 5, 9, 33, 0, 9, 4, 33])
let query = """
arr[0]=12\
&arr[1]=45\
&arr[2]=54\
&arr[3]=55\
&arr[4]=-5\
&arr[5]=5\
&arr[6]=9\
&arr[7]=33\
&arr[8]=0\
&arr[9]=9\
&arr[10]=4\
&arr[11]=33
"""
self.testForm(test2, query: query)
}

func testArrayWithIndicesThrows() {
struct Test: Codable, Equatable {
let arr: [Int]
}
let decoder = URLEncodedFormDecoder()
// incorrect indices
let query = "arr[0]=2&arr[2]=4"
XCTAssertThrowsError(try decoder.decode(Test.self, from: query)) { error in
XCTAssertEqual(error as? URLEncodedFormNode.Error, URLEncodedFormNode.Error.invalidArrayIndex(2))
}
}

func testStringSpecialCharactersDecode() {
struct Test: Codable, Equatable {
let a: String
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,11 @@ class URLEncodedFormNodeTests: XCTestCase {
XCTAssertEqual(values, [.map("array"), .array])
}

func testKeyParserArrayWithIndices() {
let values = KeyParser.parse("array[0]")
XCTAssertEqual(values, [.map("array"), .arrayWithIndices(0)])
}

func testKeyParserMap() {
let values = KeyParser.parse("array[object]")
XCTAssertEqual(values, [.map("array"), .map("object")])
Expand Down

0 comments on commit 8927764

Please sign in to comment.