-
-
Notifications
You must be signed in to change notification settings - Fork 1.4k
/
URLEncodedFormSerializer.swift
81 lines (70 loc) 路 2.63 KB
/
URLEncodedFormSerializer.swift
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
import struct Foundation.CharacterSet
struct URLEncodedFormSerializer: Sendable {
let splitVariablesOn: Character
let splitKeyValueOn: Character
/// Create a new form-urlencoded data parser.
init(splitVariablesOn: Character = "&", splitKeyValueOn: Character = "=") {
self.splitVariablesOn = splitVariablesOn
self.splitKeyValueOn = splitKeyValueOn
}
func serialize(_ data: URLEncodedFormData, codingPath: [CodingKey] = []) throws -> String {
var entries: [String] = []
let key = try codingPath.toURLEncodedKey()
for value in data.values {
if codingPath.count == 0 {
try entries.append(value.asUrlEncoded())
} else {
try entries.append(key + String(splitKeyValueOn) + value.asUrlEncoded())
}
}
for (key, child) in data.children {
entries.append(try serialize(child, codingPath: codingPath + [_CodingKey(stringValue: key) as CodingKey]))
}
return entries.joined(separator: String(splitVariablesOn))
}
struct _CodingKey: CodingKey {
var stringValue: String
init(stringValue: String) {
self.stringValue = stringValue
}
var intValue: Int?
init?(intValue: Int) {
self.intValue = intValue
self.stringValue = intValue.description
}
}
}
extension Array where Element == CodingKey {
func toURLEncodedKey() throws -> String {
if count < 1 {
return ""
}
return try self[0].stringValue.urlEncoded(codingPath: self) + self[1...].map({ (key: CodingKey) -> String in
return try "[" + key.stringValue.urlEncoded(codingPath: self) + "]"
}).joined()
}
}
// MARK: Utilities
extension String {
/// Prepares a `String` for inclusion in form-urlencoded data.
func urlEncoded(codingPath: [CodingKey] = []) throws -> String {
guard let result = self.addingPercentEncoding(
withAllowedCharacters: Characters.allowedCharacters
) else {
throw EncodingError.invalidValue(self, EncodingError.Context(
codingPath: codingPath,
debugDescription: "Unable to add percent encoding to \(self)"
))
}
return result
}
}
/// Characters allowed in form-urlencoded data.
private enum Characters {
static let allowedCharacters: CharacterSet = {
var allowed = CharacterSet.urlQueryAllowed
// these symbols are reserved for url-encoded form
allowed.remove(charactersIn: "?&=[];+")
return allowed
}()
}