Proyectos de Subversion Iphone Microlearning

Rev

| Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1 efrain 1
//
2
//  URLEncodedFormEncoder.swift
3
//
4
//  Copyright (c) 2019 Alamofire Software Foundation (http://alamofire.org/)
5
//
6
//  Permission is hereby granted, free of charge, to any person obtaining a copy
7
//  of this software and associated documentation files (the "Software"), to deal
8
//  in the Software without restriction, including without limitation the rights
9
//  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
//  copies of the Software, and to permit persons to whom the Software is
11
//  furnished to do so, subject to the following conditions:
12
//
13
//  The above copyright notice and this permission notice shall be included in
14
//  all copies or substantial portions of the Software.
15
//
16
//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
//  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
//  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
//  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
//  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
//  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22
//  THE SOFTWARE.
23
//
24
 
25
import Foundation
26
 
27
/// An object that encodes instances into URL-encoded query strings.
28
///
29
/// There is no published specification for how to encode collection types. By default, the convention of appending
30
/// `[]` to the key for array values (`foo[]=1&foo[]=2`), and appending the key surrounded by square brackets for
31
/// nested dictionary values (`foo[bar]=baz`) is used. Optionally, `ArrayEncoding` can be used to omit the
32
/// square brackets appended to array keys.
33
///
34
/// `BoolEncoding` can be used to configure how `Bool` values are encoded. The default behavior is to encode
35
/// `true` as 1 and `false` as 0.
36
///
37
/// `DateEncoding` can be used to configure how `Date` values are encoded. By default, the `.deferredToDate`
38
/// strategy is used, which formats dates from their structure.
39
///
40
/// `SpaceEncoding` can be used to configure how spaces are encoded. Modern encodings use percent replacement (`%20`),
41
/// while older encodings may expect spaces to be replaced with `+`.
42
///
43
/// This type is largely based on Vapor's [`url-encoded-form`](https://github.com/vapor/url-encoded-form) project.
44
public final class URLEncodedFormEncoder {
45
    /// Encoding to use for `Array` values.
46
    public enum ArrayEncoding {
47
        /// An empty set of square brackets ("[]") are appended to the key for every value. This is the default encoding.
48
        case brackets
49
        /// No brackets are appended to the key and the key is encoded as is.
50
        case noBrackets
51
        /// Brackets containing the item index are appended. This matches the jQuery and Node.js behavior.
52
        case indexInBrackets
53
 
54
        /// Encodes the key according to the encoding.
55
        ///
56
        /// - Parameters:
57
        ///     - key:      The `key` to encode.
58
        ///     - index:   When this enum instance is `.indexInBrackets`, the `index` to encode.
59
        ///
60
        /// - Returns:          The encoded key.
61
        func encode(_ key: String, atIndex index: Int) -> String {
62
            switch self {
63
            case .brackets: return "\(key)[]"
64
            case .noBrackets: return key
65
            case .indexInBrackets: return "\(key)[\(index)]"
66
            }
67
        }
68
    }
69
 
70
    /// Encoding to use for `Bool` values.
71
    public enum BoolEncoding {
72
        /// Encodes `true` as `1`, `false` as `0`.
73
        case numeric
74
        /// Encodes `true` as "true", `false` as "false". This is the default encoding.
75
        case literal
76
 
77
        /// Encodes the given `Bool` as a `String`.
78
        ///
79
        /// - Parameter value: The `Bool` to encode.
80
        ///
81
        /// - Returns:         The encoded `String`.
82
        func encode(_ value: Bool) -> String {
83
            switch self {
84
            case .numeric: return value ? "1" : "0"
85
            case .literal: return value ? "true" : "false"
86
            }
87
        }
88
    }
89
 
90
    /// Encoding to use for `Data` values.
91
    public enum DataEncoding {
92
        /// Defers encoding to the `Data` type.
93
        case deferredToData
94
        /// Encodes `Data` as a Base64-encoded string. This is the default encoding.
95
        case base64
96
        /// Encode the `Data` as a custom value encoded by the given closure.
97
        case custom((Data) throws -> String)
98
 
99
        /// Encodes `Data` according to the encoding.
100
        ///
101
        /// - Parameter data: The `Data` to encode.
102
        ///
103
        /// - Returns:        The encoded `String`, or `nil` if the `Data` should be encoded according to its
104
        ///                   `Encodable` implementation.
105
        func encode(_ data: Data) throws -> String? {
106
            switch self {
107
            case .deferredToData: return nil
108
            case .base64: return data.base64EncodedString()
109
            case let .custom(encoding): return try encoding(data)
110
            }
111
        }
112
    }
113
 
114
    /// Encoding to use for `Date` values.
115
    public enum DateEncoding {
116
        /// ISO8601 and RFC3339 formatter.
117
        private static let iso8601Formatter: ISO8601DateFormatter = {
118
            let formatter = ISO8601DateFormatter()
119
            formatter.formatOptions = .withInternetDateTime
120
            return formatter
121
        }()
122
 
123
        /// Defers encoding to the `Date` type. This is the default encoding.
124
        case deferredToDate
125
        /// Encodes `Date`s as seconds since midnight UTC on January 1, 1970.
126
        case secondsSince1970
127
        /// Encodes `Date`s as milliseconds since midnight UTC on January 1, 1970.
128
        case millisecondsSince1970
129
        /// Encodes `Date`s according to the ISO8601 and RFC3339 standards.
130
        case iso8601
131
        /// Encodes `Date`s using the given `DateFormatter`.
132
        case formatted(DateFormatter)
133
        /// Encodes `Date`s using the given closure.
134
        case custom((Date) throws -> String)
135
 
136
        /// Encodes the date according to the encoding.
137
        ///
138
        /// - Parameter date: The `Date` to encode.
139
        ///
140
        /// - Returns:        The encoded `String`, or `nil` if the `Date` should be encoded according to its
141
        ///                   `Encodable` implementation.
142
        func encode(_ date: Date) throws -> String? {
143
            switch self {
144
            case .deferredToDate:
145
                return nil
146
            case .secondsSince1970:
147
                return String(date.timeIntervalSince1970)
148
            case .millisecondsSince1970:
149
                return String(date.timeIntervalSince1970 * 1000.0)
150
            case .iso8601:
151
                return DateEncoding.iso8601Formatter.string(from: date)
152
            case let .formatted(formatter):
153
                return formatter.string(from: date)
154
            case let .custom(closure):
155
                return try closure(date)
156
            }
157
        }
158
    }
159
 
160
    /// Encoding to use for keys.
161
    ///
162
    /// This type is derived from [`JSONEncoder`'s `KeyEncodingStrategy`](https://github.com/apple/swift/blob/6aa313b8dd5f05135f7f878eccc1db6f9fbe34ff/stdlib/public/Darwin/Foundation/JSONEncoder.swift#L128)
163
    /// and [`XMLEncoder`s `KeyEncodingStrategy`](https://github.com/MaxDesiatov/XMLCoder/blob/master/Sources/XMLCoder/Encoder/XMLEncoder.swift#L102).
164
    public enum KeyEncoding {
165
        /// Use the keys specified by each type. This is the default encoding.
166
        case useDefaultKeys
167
        /// Convert from "camelCaseKeys" to "snake_case_keys" before writing a key.
168
        ///
169
        /// Capital characters are determined by testing membership in
170
        /// `CharacterSet.uppercaseLetters` and `CharacterSet.lowercaseLetters`
171
        /// (Unicode General Categories Lu and Lt).
172
        /// The conversion to lower case uses `Locale.system`, also known as
173
        /// the ICU "root" locale. This means the result is consistent
174
        /// regardless of the current user's locale and language preferences.
175
        ///
176
        /// Converting from camel case to snake case:
177
        /// 1. Splits words at the boundary of lower-case to upper-case
178
        /// 2. Inserts `_` between words
179
        /// 3. Lowercases the entire string
180
        /// 4. Preserves starting and ending `_`.
181
        ///
182
        /// For example, `oneTwoThree` becomes `one_two_three`. `_oneTwoThree_` becomes `_one_two_three_`.
183
        ///
184
        /// - Note: Using a key encoding strategy has a nominal performance cost, as each string key has to be converted.
185
        case convertToSnakeCase
186
        /// Same as convertToSnakeCase, but using `-` instead of `_`.
187
        /// For example `oneTwoThree` becomes `one-two-three`.
188
        case convertToKebabCase
189
        /// Capitalize the first letter only.
190
        /// For example `oneTwoThree` becomes  `OneTwoThree`.
191
        case capitalized
192
        /// Uppercase all letters.
193
        /// For example `oneTwoThree` becomes  `ONETWOTHREE`.
194
        case uppercased
195
        /// Lowercase all letters.
196
        /// For example `oneTwoThree` becomes  `onetwothree`.
197
        case lowercased
198
        /// A custom encoding using the provided closure.
199
        case custom((String) -> String)
200
 
201
        func encode(_ key: String) -> String {
202
            switch self {
203
            case .useDefaultKeys: return key
204
            case .convertToSnakeCase: return convertToSnakeCase(key)
205
            case .convertToKebabCase: return convertToKebabCase(key)
206
            case .capitalized: return String(key.prefix(1).uppercased() + key.dropFirst())
207
            case .uppercased: return key.uppercased()
208
            case .lowercased: return key.lowercased()
209
            case let .custom(encoding): return encoding(key)
210
            }
211
        }
212
 
213
        private func convertToSnakeCase(_ key: String) -> String {
214
            convert(key, usingSeparator: "_")
215
        }
216
 
217
        private func convertToKebabCase(_ key: String) -> String {
218
            convert(key, usingSeparator: "-")
219
        }
220
 
221
        private func convert(_ key: String, usingSeparator separator: String) -> String {
222
            guard !key.isEmpty else { return key }
223
 
224
            var words: [Range<String.Index>] = []
225
            // The general idea of this algorithm is to split words on
226
            // transition from lower to upper case, then on transition of >1
227
            // upper case characters to lowercase
228
            //
229
            // myProperty -> my_property
230
            // myURLProperty -> my_url_property
231
            //
232
            // It is assumed, per Swift naming conventions, that the first character of the key is lowercase.
233
            var wordStart = key.startIndex
234
            var searchRange = key.index(after: wordStart)..<key.endIndex
235
 
236
            // Find next uppercase character
237
            while let upperCaseRange = key.rangeOfCharacter(from: CharacterSet.uppercaseLetters, options: [], range: searchRange) {
238
                let untilUpperCase = wordStart..<upperCaseRange.lowerBound
239
                words.append(untilUpperCase)
240
 
241
                // Find next lowercase character
242
                searchRange = upperCaseRange.lowerBound..<searchRange.upperBound
243
                guard let lowerCaseRange = key.rangeOfCharacter(from: CharacterSet.lowercaseLetters, options: [], range: searchRange) else {
244
                    // There are no more lower case letters. Just end here.
245
                    wordStart = searchRange.lowerBound
246
                    break
247
                }
248
 
249
                // Is the next lowercase letter more than 1 after the uppercase?
250
                // If so, we encountered a group of uppercase letters that we
251
                // should treat as its own word
252
                let nextCharacterAfterCapital = key.index(after: upperCaseRange.lowerBound)
253
                if lowerCaseRange.lowerBound == nextCharacterAfterCapital {
254
                    // The next character after capital is a lower case character and therefore not a word boundary.
255
                    // Continue searching for the next upper case for the boundary.
256
                    wordStart = upperCaseRange.lowerBound
257
                } else {
258
                    // There was a range of >1 capital letters. Turn those into a word, stopping at the capital before the lower case character.
259
                    let beforeLowerIndex = key.index(before: lowerCaseRange.lowerBound)
260
                    words.append(upperCaseRange.lowerBound..<beforeLowerIndex)
261
 
262
                    // Next word starts at the capital before the lowercase we just found
263
                    wordStart = beforeLowerIndex
264
                }
265
                searchRange = lowerCaseRange.upperBound..<searchRange.upperBound
266
            }
267
            words.append(wordStart..<searchRange.upperBound)
268
            let result = words.map { range in
269
                key[range].lowercased()
270
            }.joined(separator: separator)
271
 
272
            return result
273
        }
274
    }
275
 
276
    /// Encoding to use for spaces.
277
    public enum SpaceEncoding {
278
        /// Encodes spaces according to normal percent escaping rules (%20).
279
        case percentEscaped
280
        /// Encodes spaces as `+`,
281
        case plusReplaced
282
 
283
        /// Encodes the string according to the encoding.
284
        ///
285
        /// - Parameter string: The `String` to encode.
286
        ///
287
        /// - Returns:          The encoded `String`.
288
        func encode(_ string: String) -> String {
289
            switch self {
290
            case .percentEscaped: return string.replacingOccurrences(of: " ", with: "%20")
291
            case .plusReplaced: return string.replacingOccurrences(of: " ", with: "+")
292
            }
293
        }
294
    }
295
 
296
    /// `URLEncodedFormEncoder` error.
297
    public enum Error: Swift.Error {
298
        /// An invalid root object was created by the encoder. Only keyed values are valid.
299
        case invalidRootObject(String)
300
 
301
        var localizedDescription: String {
302
            switch self {
303
            case let .invalidRootObject(object):
304
                return "URLEncodedFormEncoder requires keyed root object. Received \(object) instead."
305
            }
306
        }
307
    }
308
 
309
    /// Whether or not to sort the encoded key value pairs.
310
    ///
311
    /// - Note: This setting ensures a consistent ordering for all encodings of the same parameters. When set to `false`,
312
    ///         encoded `Dictionary` values may have a different encoded order each time they're encoded due to
313
    ///       ` Dictionary`'s random storage order, but `Encodable` types will maintain their encoded order.
314
    public let alphabetizeKeyValuePairs: Bool
315
    /// The `ArrayEncoding` to use.
316
    public let arrayEncoding: ArrayEncoding
317
    /// The `BoolEncoding` to use.
318
    public let boolEncoding: BoolEncoding
319
    /// THe `DataEncoding` to use.
320
    public let dataEncoding: DataEncoding
321
    /// The `DateEncoding` to use.
322
    public let dateEncoding: DateEncoding
323
    /// The `KeyEncoding` to use.
324
    public let keyEncoding: KeyEncoding
325
    /// The `SpaceEncoding` to use.
326
    public let spaceEncoding: SpaceEncoding
327
    /// The `CharacterSet` of allowed (non-escaped) characters.
328
    public var allowedCharacters: CharacterSet
329
 
330
    /// Creates an instance from the supplied parameters.
331
    ///
332
    /// - Parameters:
333
    ///   - alphabetizeKeyValuePairs: Whether or not to sort the encoded key value pairs. `true` by default.
334
    ///   - arrayEncoding:            The `ArrayEncoding` to use. `.brackets` by default.
335
    ///   - boolEncoding:             The `BoolEncoding` to use. `.numeric` by default.
336
    ///   - dataEncoding:             The `DataEncoding` to use. `.base64` by default.
337
    ///   - dateEncoding:             The `DateEncoding` to use. `.deferredToDate` by default.
338
    ///   - keyEncoding:              The `KeyEncoding` to use. `.useDefaultKeys` by default.
339
    ///   - spaceEncoding:            The `SpaceEncoding` to use. `.percentEscaped` by default.
340
    ///   - allowedCharacters:        The `CharacterSet` of allowed (non-escaped) characters. `.afURLQueryAllowed` by
341
    ///                               default.
342
    public init(alphabetizeKeyValuePairs: Bool = true,
343
                arrayEncoding: ArrayEncoding = .brackets,
344
                boolEncoding: BoolEncoding = .numeric,
345
                dataEncoding: DataEncoding = .base64,
346
                dateEncoding: DateEncoding = .deferredToDate,
347
                keyEncoding: KeyEncoding = .useDefaultKeys,
348
                spaceEncoding: SpaceEncoding = .percentEscaped,
349
                allowedCharacters: CharacterSet = .afURLQueryAllowed) {
350
        self.alphabetizeKeyValuePairs = alphabetizeKeyValuePairs
351
        self.arrayEncoding = arrayEncoding
352
        self.boolEncoding = boolEncoding
353
        self.dataEncoding = dataEncoding
354
        self.dateEncoding = dateEncoding
355
        self.keyEncoding = keyEncoding
356
        self.spaceEncoding = spaceEncoding
357
        self.allowedCharacters = allowedCharacters
358
    }
359
 
360
    func encode(_ value: Encodable) throws -> URLEncodedFormComponent {
361
        let context = URLEncodedFormContext(.object([]))
362
        let encoder = _URLEncodedFormEncoder(context: context,
363
                                             boolEncoding: boolEncoding,
364
                                             dataEncoding: dataEncoding,
365
                                             dateEncoding: dateEncoding)
366
        try value.encode(to: encoder)
367
 
368
        return context.component
369
    }
370
 
371
    /// Encodes the `value` as a URL form encoded `String`.
372
    ///
373
    /// - Parameter value: The `Encodable` value.`
374
    ///
375
    /// - Returns:         The encoded `String`.
376
    /// - Throws:          An `Error` or `EncodingError` instance if encoding fails.
377
    public func encode(_ value: Encodable) throws -> String {
378
        let component: URLEncodedFormComponent = try encode(value)
379
 
380
        guard case let .object(object) = component else {
381
            throw Error.invalidRootObject("\(component)")
382
        }
383
 
384
        let serializer = URLEncodedFormSerializer(alphabetizeKeyValuePairs: alphabetizeKeyValuePairs,
385
                                                  arrayEncoding: arrayEncoding,
386
                                                  keyEncoding: keyEncoding,
387
                                                  spaceEncoding: spaceEncoding,
388
                                                  allowedCharacters: allowedCharacters)
389
        let query = serializer.serialize(object)
390
 
391
        return query
392
    }
393
 
394
    /// Encodes the value as `Data`. This is performed by first creating an encoded `String` and then returning the
395
    /// `.utf8` data.
396
    ///
397
    /// - Parameter value: The `Encodable` value.
398
    ///
399
    /// - Returns:         The encoded `Data`.
400
    ///
401
    /// - Throws:          An `Error` or `EncodingError` instance if encoding fails.
402
    public func encode(_ value: Encodable) throws -> Data {
403
        let string: String = try encode(value)
404
 
405
        return Data(string.utf8)
406
    }
407
}
408
 
409
final class _URLEncodedFormEncoder {
410
    var codingPath: [CodingKey]
411
    // Returns an empty dictionary, as this encoder doesn't support userInfo.
412
    var userInfo: [CodingUserInfoKey: Any] { [:] }
413
 
414
    let context: URLEncodedFormContext
415
 
416
    private let boolEncoding: URLEncodedFormEncoder.BoolEncoding
417
    private let dataEncoding: URLEncodedFormEncoder.DataEncoding
418
    private let dateEncoding: URLEncodedFormEncoder.DateEncoding
419
 
420
    init(context: URLEncodedFormContext,
421
         codingPath: [CodingKey] = [],
422
         boolEncoding: URLEncodedFormEncoder.BoolEncoding,
423
         dataEncoding: URLEncodedFormEncoder.DataEncoding,
424
         dateEncoding: URLEncodedFormEncoder.DateEncoding) {
425
        self.context = context
426
        self.codingPath = codingPath
427
        self.boolEncoding = boolEncoding
428
        self.dataEncoding = dataEncoding
429
        self.dateEncoding = dateEncoding
430
    }
431
}
432
 
433
extension _URLEncodedFormEncoder: Encoder {
434
    func container<Key>(keyedBy type: Key.Type) -> KeyedEncodingContainer<Key> where Key: CodingKey {
435
        let container = _URLEncodedFormEncoder.KeyedContainer<Key>(context: context,
436
                                                                   codingPath: codingPath,
437
                                                                   boolEncoding: boolEncoding,
438
                                                                   dataEncoding: dataEncoding,
439
                                                                   dateEncoding: dateEncoding)
440
        return KeyedEncodingContainer(container)
441
    }
442
 
443
    func unkeyedContainer() -> UnkeyedEncodingContainer {
444
        _URLEncodedFormEncoder.UnkeyedContainer(context: context,
445
                                                codingPath: codingPath,
446
                                                boolEncoding: boolEncoding,
447
                                                dataEncoding: dataEncoding,
448
                                                dateEncoding: dateEncoding)
449
    }
450
 
451
    func singleValueContainer() -> SingleValueEncodingContainer {
452
        _URLEncodedFormEncoder.SingleValueContainer(context: context,
453
                                                    codingPath: codingPath,
454
                                                    boolEncoding: boolEncoding,
455
                                                    dataEncoding: dataEncoding,
456
                                                    dateEncoding: dateEncoding)
457
    }
458
}
459
 
460
final class URLEncodedFormContext {
461
    var component: URLEncodedFormComponent
462
 
463
    init(_ component: URLEncodedFormComponent) {
464
        self.component = component
465
    }
466
}
467
 
468
enum URLEncodedFormComponent {
469
    typealias Object = [(key: String, value: URLEncodedFormComponent)]
470
 
471
    case string(String)
472
    case array([URLEncodedFormComponent])
473
    case object(Object)
474
 
475
    /// Converts self to an `[URLEncodedFormData]` or returns `nil` if not convertible.
476
    var array: [URLEncodedFormComponent]? {
477
        switch self {
478
        case let .array(array): return array
479
        default: return nil
480
        }
481
    }
482
 
483
    /// Converts self to an `Object` or returns `nil` if not convertible.
484
    var object: Object? {
485
        switch self {
486
        case let .object(object): return object
487
        default: return nil
488
        }
489
    }
490
 
491
    /// Sets self to the supplied value at a given path.
492
    ///
493
    ///     data.set(to: "hello", at: ["path", "to", "value"])
494
    ///
495
    /// - parameters:
496
    ///     - value: Value of `Self` to set at the supplied path.
497
    ///     - path: `CodingKey` path to update with the supplied value.
498
    public mutating func set(to value: URLEncodedFormComponent, at path: [CodingKey]) {
499
        set(&self, to: value, at: path)
500
    }
501
 
502
    /// Recursive backing method to `set(to:at:)`.
503
    private func set(_ context: inout URLEncodedFormComponent, to value: URLEncodedFormComponent, at path: [CodingKey]) {
504
        guard !path.isEmpty else {
505
            context = value
506
            return
507
        }
508
 
509
        let end = path[0]
510
        var child: URLEncodedFormComponent
511
        switch path.count {
512
        case 1:
513
            child = value
514
        case 2...:
515
            if let index = end.intValue {
516
                let array = context.array ?? []
517
                if array.count > index {
518
                    child = array[index]
519
                } else {
520
                    child = .array([])
521
                }
522
                set(&child, to: value, at: Array(path[1...]))
523
            } else {
524
                child = context.object?.first { $0.key == end.stringValue }?.value ?? .object(.init())
525
                set(&child, to: value, at: Array(path[1...]))
526
            }
527
        default: fatalError("Unreachable")
528
        }
529
 
530
        if let index = end.intValue {
531
            if var array = context.array {
532
                if array.count > index {
533
                    array[index] = child
534
                } else {
535
                    array.append(child)
536
                }
537
                context = .array(array)
538
            } else {
539
                context = .array([child])
540
            }
541
        } else {
542
            if var object = context.object {
543
                if let index = object.firstIndex(where: { $0.key == end.stringValue }) {
544
                    object[index] = (key: end.stringValue, value: child)
545
                } else {
546
                    object.append((key: end.stringValue, value: child))
547
                }
548
                context = .object(object)
549
            } else {
550
                context = .object([(key: end.stringValue, value: child)])
551
            }
552
        }
553
    }
554
}
555
 
556
struct AnyCodingKey: CodingKey, Hashable {
557
    let stringValue: String
558
    let intValue: Int?
559
 
560
    init?(stringValue: String) {
561
        self.stringValue = stringValue
562
        intValue = nil
563
    }
564
 
565
    init?(intValue: Int) {
566
        stringValue = "\(intValue)"
567
        self.intValue = intValue
568
    }
569
 
570
    init<Key>(_ base: Key) where Key: CodingKey {
571
        if let intValue = base.intValue {
572
            self.init(intValue: intValue)!
573
        } else {
574
            self.init(stringValue: base.stringValue)!
575
        }
576
    }
577
}
578
 
579
extension _URLEncodedFormEncoder {
580
    final class KeyedContainer<Key> where Key: CodingKey {
581
        var codingPath: [CodingKey]
582
 
583
        private let context: URLEncodedFormContext
584
        private let boolEncoding: URLEncodedFormEncoder.BoolEncoding
585
        private let dataEncoding: URLEncodedFormEncoder.DataEncoding
586
        private let dateEncoding: URLEncodedFormEncoder.DateEncoding
587
 
588
        init(context: URLEncodedFormContext,
589
             codingPath: [CodingKey],
590
             boolEncoding: URLEncodedFormEncoder.BoolEncoding,
591
             dataEncoding: URLEncodedFormEncoder.DataEncoding,
592
             dateEncoding: URLEncodedFormEncoder.DateEncoding) {
593
            self.context = context
594
            self.codingPath = codingPath
595
            self.boolEncoding = boolEncoding
596
            self.dataEncoding = dataEncoding
597
            self.dateEncoding = dateEncoding
598
        }
599
 
600
        private func nestedCodingPath(for key: CodingKey) -> [CodingKey] {
601
            codingPath + [key]
602
        }
603
    }
604
}
605
 
606
extension _URLEncodedFormEncoder.KeyedContainer: KeyedEncodingContainerProtocol {
607
    func encodeNil(forKey key: Key) throws {
608
        let context = EncodingError.Context(codingPath: codingPath,
609
                                            debugDescription: "URLEncodedFormEncoder cannot encode nil values.")
610
        throw EncodingError.invalidValue("\(key): nil", context)
611
    }
612
 
613
    func encode<T>(_ value: T, forKey key: Key) throws where T: Encodable {
614
        var container = nestedSingleValueEncoder(for: key)
615
        try container.encode(value)
616
    }
617
 
618
    func nestedSingleValueEncoder(for key: Key) -> SingleValueEncodingContainer {
619
        let container = _URLEncodedFormEncoder.SingleValueContainer(context: context,
620
                                                                    codingPath: nestedCodingPath(for: key),
621
                                                                    boolEncoding: boolEncoding,
622
                                                                    dataEncoding: dataEncoding,
623
                                                                    dateEncoding: dateEncoding)
624
 
625
        return container
626
    }
627
 
628
    func nestedUnkeyedContainer(forKey key: Key) -> UnkeyedEncodingContainer {
629
        let container = _URLEncodedFormEncoder.UnkeyedContainer(context: context,
630
                                                                codingPath: nestedCodingPath(for: key),
631
                                                                boolEncoding: boolEncoding,
632
                                                                dataEncoding: dataEncoding,
633
                                                                dateEncoding: dateEncoding)
634
 
635
        return container
636
    }
637
 
638
    func nestedContainer<NestedKey>(keyedBy keyType: NestedKey.Type, forKey key: Key) -> KeyedEncodingContainer<NestedKey> where NestedKey: CodingKey {
639
        let container = _URLEncodedFormEncoder.KeyedContainer<NestedKey>(context: context,
640
                                                                         codingPath: nestedCodingPath(for: key),
641
                                                                         boolEncoding: boolEncoding,
642
                                                                         dataEncoding: dataEncoding,
643
                                                                         dateEncoding: dateEncoding)
644
 
645
        return KeyedEncodingContainer(container)
646
    }
647
 
648
    func superEncoder() -> Encoder {
649
        _URLEncodedFormEncoder(context: context,
650
                               codingPath: codingPath,
651
                               boolEncoding: boolEncoding,
652
                               dataEncoding: dataEncoding,
653
                               dateEncoding: dateEncoding)
654
    }
655
 
656
    func superEncoder(forKey key: Key) -> Encoder {
657
        _URLEncodedFormEncoder(context: context,
658
                               codingPath: nestedCodingPath(for: key),
659
                               boolEncoding: boolEncoding,
660
                               dataEncoding: dataEncoding,
661
                               dateEncoding: dateEncoding)
662
    }
663
}
664
 
665
extension _URLEncodedFormEncoder {
666
    final class SingleValueContainer {
667
        var codingPath: [CodingKey]
668
 
669
        private var canEncodeNewValue = true
670
 
671
        private let context: URLEncodedFormContext
672
        private let boolEncoding: URLEncodedFormEncoder.BoolEncoding
673
        private let dataEncoding: URLEncodedFormEncoder.DataEncoding
674
        private let dateEncoding: URLEncodedFormEncoder.DateEncoding
675
 
676
        init(context: URLEncodedFormContext,
677
             codingPath: [CodingKey],
678
             boolEncoding: URLEncodedFormEncoder.BoolEncoding,
679
             dataEncoding: URLEncodedFormEncoder.DataEncoding,
680
             dateEncoding: URLEncodedFormEncoder.DateEncoding) {
681
            self.context = context
682
            self.codingPath = codingPath
683
            self.boolEncoding = boolEncoding
684
            self.dataEncoding = dataEncoding
685
            self.dateEncoding = dateEncoding
686
        }
687
 
688
        private func checkCanEncode(value: Any?) throws {
689
            guard canEncodeNewValue else {
690
                let context = EncodingError.Context(codingPath: codingPath,
691
                                                    debugDescription: "Attempt to encode value through single value container when previously value already encoded.")
692
                throw EncodingError.invalidValue(value as Any, context)
693
            }
694
        }
695
    }
696
}
697
 
698
extension _URLEncodedFormEncoder.SingleValueContainer: SingleValueEncodingContainer {
699
    func encodeNil() throws {
700
        try checkCanEncode(value: nil)
701
        defer { canEncodeNewValue = false }
702
 
703
        let context = EncodingError.Context(codingPath: codingPath,
704
                                            debugDescription: "URLEncodedFormEncoder cannot encode nil values.")
705
        throw EncodingError.invalidValue("nil", context)
706
    }
707
 
708
    func encode(_ value: Bool) throws {
709
        try encode(value, as: String(boolEncoding.encode(value)))
710
    }
711
 
712
    func encode(_ value: String) throws {
713
        try encode(value, as: value)
714
    }
715
 
716
    func encode(_ value: Double) throws {
717
        try encode(value, as: String(value))
718
    }
719
 
720
    func encode(_ value: Float) throws {
721
        try encode(value, as: String(value))
722
    }
723
 
724
    func encode(_ value: Int) throws {
725
        try encode(value, as: String(value))
726
    }
727
 
728
    func encode(_ value: Int8) throws {
729
        try encode(value, as: String(value))
730
    }
731
 
732
    func encode(_ value: Int16) throws {
733
        try encode(value, as: String(value))
734
    }
735
 
736
    func encode(_ value: Int32) throws {
737
        try encode(value, as: String(value))
738
    }
739
 
740
    func encode(_ value: Int64) throws {
741
        try encode(value, as: String(value))
742
    }
743
 
744
    func encode(_ value: UInt) throws {
745
        try encode(value, as: String(value))
746
    }
747
 
748
    func encode(_ value: UInt8) throws {
749
        try encode(value, as: String(value))
750
    }
751
 
752
    func encode(_ value: UInt16) throws {
753
        try encode(value, as: String(value))
754
    }
755
 
756
    func encode(_ value: UInt32) throws {
757
        try encode(value, as: String(value))
758
    }
759
 
760
    func encode(_ value: UInt64) throws {
761
        try encode(value, as: String(value))
762
    }
763
 
764
    private func encode<T>(_ value: T, as string: String) throws where T: Encodable {
765
        try checkCanEncode(value: value)
766
        defer { canEncodeNewValue = false }
767
 
768
        context.component.set(to: .string(string), at: codingPath)
769
    }
770
 
771
    func encode<T>(_ value: T) throws where T: Encodable {
772
        switch value {
773
        case let date as Date:
774
            guard let string = try dateEncoding.encode(date) else {
775
                try attemptToEncode(value)
776
                return
777
            }
778
 
779
            try encode(value, as: string)
780
        case let data as Data:
781
            guard let string = try dataEncoding.encode(data) else {
782
                try attemptToEncode(value)
783
                return
784
            }
785
 
786
            try encode(value, as: string)
787
        case let decimal as Decimal:
788
            // Decimal's `Encodable` implementation returns an object, not a single value, so override it.
789
            try encode(value, as: String(describing: decimal))
790
        default:
791
            try attemptToEncode(value)
792
        }
793
    }
794
 
795
    private func attemptToEncode<T>(_ value: T) throws where T: Encodable {
796
        try checkCanEncode(value: value)
797
        defer { canEncodeNewValue = false }
798
 
799
        let encoder = _URLEncodedFormEncoder(context: context,
800
                                             codingPath: codingPath,
801
                                             boolEncoding: boolEncoding,
802
                                             dataEncoding: dataEncoding,
803
                                             dateEncoding: dateEncoding)
804
        try value.encode(to: encoder)
805
    }
806
}
807
 
808
extension _URLEncodedFormEncoder {
809
    final class UnkeyedContainer {
810
        var codingPath: [CodingKey]
811
 
812
        var count = 0
813
        var nestedCodingPath: [CodingKey] {
814
            codingPath + [AnyCodingKey(intValue: count)!]
815
        }
816
 
817
        private let context: URLEncodedFormContext
818
        private let boolEncoding: URLEncodedFormEncoder.BoolEncoding
819
        private let dataEncoding: URLEncodedFormEncoder.DataEncoding
820
        private let dateEncoding: URLEncodedFormEncoder.DateEncoding
821
 
822
        init(context: URLEncodedFormContext,
823
             codingPath: [CodingKey],
824
             boolEncoding: URLEncodedFormEncoder.BoolEncoding,
825
             dataEncoding: URLEncodedFormEncoder.DataEncoding,
826
             dateEncoding: URLEncodedFormEncoder.DateEncoding) {
827
            self.context = context
828
            self.codingPath = codingPath
829
            self.boolEncoding = boolEncoding
830
            self.dataEncoding = dataEncoding
831
            self.dateEncoding = dateEncoding
832
        }
833
    }
834
}
835
 
836
extension _URLEncodedFormEncoder.UnkeyedContainer: UnkeyedEncodingContainer {
837
    func encodeNil() throws {
838
        let context = EncodingError.Context(codingPath: codingPath,
839
                                            debugDescription: "URLEncodedFormEncoder cannot encode nil values.")
840
        throw EncodingError.invalidValue("nil", context)
841
    }
842
 
843
    func encode<T>(_ value: T) throws where T: Encodable {
844
        var container = nestedSingleValueContainer()
845
        try container.encode(value)
846
    }
847
 
848
    func nestedSingleValueContainer() -> SingleValueEncodingContainer {
849
        defer { count += 1 }
850
 
851
        return _URLEncodedFormEncoder.SingleValueContainer(context: context,
852
                                                           codingPath: nestedCodingPath,
853
                                                           boolEncoding: boolEncoding,
854
                                                           dataEncoding: dataEncoding,
855
                                                           dateEncoding: dateEncoding)
856
    }
857
 
858
    func nestedContainer<NestedKey>(keyedBy keyType: NestedKey.Type) -> KeyedEncodingContainer<NestedKey> where NestedKey: CodingKey {
859
        defer { count += 1 }
860
        let container = _URLEncodedFormEncoder.KeyedContainer<NestedKey>(context: context,
861
                                                                         codingPath: nestedCodingPath,
862
                                                                         boolEncoding: boolEncoding,
863
                                                                         dataEncoding: dataEncoding,
864
                                                                         dateEncoding: dateEncoding)
865
 
866
        return KeyedEncodingContainer(container)
867
    }
868
 
869
    func nestedUnkeyedContainer() -> UnkeyedEncodingContainer {
870
        defer { count += 1 }
871
 
872
        return _URLEncodedFormEncoder.UnkeyedContainer(context: context,
873
                                                       codingPath: nestedCodingPath,
874
                                                       boolEncoding: boolEncoding,
875
                                                       dataEncoding: dataEncoding,
876
                                                       dateEncoding: dateEncoding)
877
    }
878
 
879
    func superEncoder() -> Encoder {
880
        defer { count += 1 }
881
 
882
        return _URLEncodedFormEncoder(context: context,
883
                                      codingPath: codingPath,
884
                                      boolEncoding: boolEncoding,
885
                                      dataEncoding: dataEncoding,
886
                                      dateEncoding: dateEncoding)
887
    }
888
}
889
 
890
final class URLEncodedFormSerializer {
891
    private let alphabetizeKeyValuePairs: Bool
892
    private let arrayEncoding: URLEncodedFormEncoder.ArrayEncoding
893
    private let keyEncoding: URLEncodedFormEncoder.KeyEncoding
894
    private let spaceEncoding: URLEncodedFormEncoder.SpaceEncoding
895
    private let allowedCharacters: CharacterSet
896
 
897
    init(alphabetizeKeyValuePairs: Bool,
898
         arrayEncoding: URLEncodedFormEncoder.ArrayEncoding,
899
         keyEncoding: URLEncodedFormEncoder.KeyEncoding,
900
         spaceEncoding: URLEncodedFormEncoder.SpaceEncoding,
901
         allowedCharacters: CharacterSet) {
902
        self.alphabetizeKeyValuePairs = alphabetizeKeyValuePairs
903
        self.arrayEncoding = arrayEncoding
904
        self.keyEncoding = keyEncoding
905
        self.spaceEncoding = spaceEncoding
906
        self.allowedCharacters = allowedCharacters
907
    }
908
 
909
    func serialize(_ object: URLEncodedFormComponent.Object) -> String {
910
        var output: [String] = []
911
        for (key, component) in object {
912
            let value = serialize(component, forKey: key)
913
            output.append(value)
914
        }
915
        output = alphabetizeKeyValuePairs ? output.sorted() : output
916
 
917
        return output.joinedWithAmpersands()
918
    }
919
 
920
    func serialize(_ component: URLEncodedFormComponent, forKey key: String) -> String {
921
        switch component {
922
        case let .string(string): return "\(escape(keyEncoding.encode(key)))=\(escape(string))"
923
        case let .array(array): return serialize(array, forKey: key)
924
        case let .object(object): return serialize(object, forKey: key)
925
        }
926
    }
927
 
928
    func serialize(_ object: URLEncodedFormComponent.Object, forKey key: String) -> String {
929
        var segments: [String] = object.map { subKey, value in
930
            let keyPath = "[\(subKey)]"
931
            return serialize(value, forKey: key + keyPath)
932
        }
933
        segments = alphabetizeKeyValuePairs ? segments.sorted() : segments
934
 
935
        return segments.joinedWithAmpersands()
936
    }
937
 
938
    func serialize(_ array: [URLEncodedFormComponent], forKey key: String) -> String {
939
        var segments: [String] = array.enumerated().map { index, component in
940
            let keyPath = arrayEncoding.encode(key, atIndex: index)
941
            return serialize(component, forKey: keyPath)
942
        }
943
        segments = alphabetizeKeyValuePairs ? segments.sorted() : segments
944
 
945
        return segments.joinedWithAmpersands()
946
    }
947
 
948
    func escape(_ query: String) -> String {
949
        var allowedCharactersWithSpace = allowedCharacters
950
        allowedCharactersWithSpace.insert(charactersIn: " ")
951
        let escapedQuery = query.addingPercentEncoding(withAllowedCharacters: allowedCharactersWithSpace) ?? query
952
        let spaceEncodedQuery = spaceEncoding.encode(escapedQuery)
953
 
954
        return spaceEncodedQuery
955
    }
956
}
957
 
958
extension Array where Element == String {
959
    func joinedWithAmpersands() -> String {
960
        joined(separator: "&")
961
    }
962
}
963
 
964
extension CharacterSet {
965
    /// Creates a CharacterSet from RFC 3986 allowed characters.
966
    ///
967
    /// RFC 3986 states that the following characters are "reserved" characters.
968
    ///
969
    /// - General Delimiters: ":", "#", "[", "]", "@", "?", "/"
970
    /// - Sub-Delimiters: "!", "$", "&", "'", "(", ")", "*", "+", ",", ";", "="
971
    ///
972
    /// In RFC 3986 - Section 3.4, it states that the "?" and "/" characters should not be escaped to allow
973
    /// query strings to include a URL. Therefore, all "reserved" characters with the exception of "?" and "/"
974
    /// should be percent-escaped in the query string.
975
    public static let afURLQueryAllowed: CharacterSet = {
976
        let generalDelimitersToEncode = ":#[]@" // does not include "?" or "/" due to RFC 3986 - Section 3.4
977
        let subDelimitersToEncode = "!$&'()*+,;="
978
        let encodableDelimiters = CharacterSet(charactersIn: "\(generalDelimitersToEncode)\(subDelimitersToEncode)")
979
 
980
        return CharacterSet.urlQueryAllowed.subtracting(encodableDelimiters)
981
    }()
982
}