this repo has no description
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Add 'Repeated' array encoding strategy

+66 -27
+1 -1
Package.swift
··· 3 3 import PackageDescription 4 4 5 5 let package = Package( 6 - name: "URLQueryItemCoder", 6 + name: "swift-urlqueryitem-encoder", 7 7 platforms: [ 8 8 .iOS(.v13), 9 9 .macOS(.v10_15),
+31
Sources/URLQueryItemCoder/Encoder/Generic Implementation/Strategies/ArrayEncodingStrategy.swift
··· 1 + // 2 + // ArrayEncodingStrategy.swift 3 + // URLQueryItemCoder 4 + // 5 + 6 + /// The strategy to use for encoding `Array` values. 7 + public enum ArrayEncodingStrategy: Sendable { 8 + /// An array encoding strategy that encodes arrays as indexed keys. 9 + /// 10 + /// Each element is encoded under a key suffixed with its zero-based index, separated by a dot. 11 + /// For example, an array `["a", "b"]` at key `"tags"` produces `tags.0=a&tags.1=b`. 12 + /// 13 + /// The `ArrayEncodingStrategy.indexed` strategy is the strategy used if you don't specify one. 14 + case indexed 15 + 16 + /// An array encoding strategy that encodes arrays as repeated keys. 17 + /// 18 + /// Each element is encoded under the same key. For example, an array `["a", "b"]` at key `"tags"` 19 + /// produces `tags=a&tags=b`. 20 + /// 21 + /// This is required by protocols such as XRPC (AT Protocol) which expect repeated query parameters 22 + /// for array values. 23 + case repeated 24 + 25 + // MARK: Public Static Interface 26 + 27 + /// The default array encoding strategy. 28 + /// 29 + /// Equals `.indexed`. 30 + public static let `default`: ArrayEncodingStrategy = .indexed 31 + }
+11 -5
Sources/URLQueryItemCoder/Encoder/Generic Implementation/Strategies/EncodingStrategies.swift
··· 9 9 public struct EncodingStrategies { 10 10 /// The collection initialized to all default values. 11 11 public static let `default` = Self() 12 - 12 + 13 + /// The strategy to use for encoding `Array` values. 14 + public var arrayStrategy: ArrayEncodingStrategy 15 + 13 16 /// The strategy to use for encoding `Data` values. 14 17 public var dataStrategy: DataEncodingStrategy 15 - 18 + 16 19 /// The strategy to use for encoding `Date` values. 17 20 public var dateStrategy: DateEncodingStrategy 18 - 21 + 19 22 /// The strategy to use for automatically changing the value of keys before encoding. 20 23 public var keyStrategy: KeyEncodingStrategy 21 - 24 + 22 25 /// The strategy to use for non-conforming floating-point values (IEEE 754 infinity and NaN). 23 26 public var nonConformingFloatStrategy: NonConformingFloatEncodingStrategy 24 - 27 + 25 28 // MARK: Public Initialization 26 29 27 30 /// Creates a new collection of encoding strategies for encoding `Encodable` values. 28 31 /// 29 32 /// The default encoding strategies are used if none are supplied. 30 33 /// 34 + /// - Parameter arrayStrategy: The strategy to use for encoding `Array` values. 31 35 /// - Parameter dataStrategy: The strategy to use for encoding `Data` values. 32 36 /// - Parameter dateStrategy: The strategy to use for encoding `Date` values. 33 37 /// - Parameter keyStrategy: The strategy to use for encoding keys on keyed containers. ··· 35 39 /// (IEEE 754 infinity and NaN). 36 40 /// - Returns: Encoding strategies for encoding `Encodable` values. 37 41 public init( 42 + arrayStrategy: ArrayEncodingStrategy = .default, 38 43 dataStrategy: DataEncodingStrategy = .default, 39 44 dateStrategy: DateEncodingStrategy = .default, 40 45 keyStrategy: KeyEncodingStrategy = .default, 41 46 nonConformingFloatStrategy: NonConformingFloatEncodingStrategy = .default 42 47 ) { 48 + self.arrayStrategy = arrayStrategy 43 49 self.dataStrategy = dataStrategy 44 50 self.dateStrategy = dateStrategy 45 51 self.keyStrategy = keyStrategy
+23 -21
Sources/URLQueryItemCoder/Encoder/URLQueryItemEncoder.swift
··· 31 31 /// - Returns: A new, reusable `URLQueryItem` encoder. 32 32 @inlinable 33 33 public init( 34 + arrayStrategy: ArrayEncodingStrategy = .default, 34 35 dataStrategy: DataEncodingStrategy = .default, 35 36 dateStrategy: DateEncodingStrategy = .default, 36 37 keyStrategy: KeyEncodingStrategy = .default, ··· 39 40 ) { 40 41 self.init( 41 42 strategies: EncodingStrategies( 43 + arrayStrategy: arrayStrategy, 42 44 dataStrategy: dataStrategy, 43 45 dateStrategy: dateStrategy, 44 46 keyStrategy: keyStrategy, ··· 65 67 private func encode( 66 68 _ container: EncodingContainer?, 67 69 at key: String = String(), 68 - into dictionaryRepresentation: inout [String: String?] 70 + into queryItems: inout [URLQueryItem] 69 71 ) { 70 72 guard let container else { 71 73 return 72 74 } 73 - 75 + 74 76 let separator = key.isEmpty ? "" : "." 75 - 77 + 76 78 switch container { 77 79 case let .keyed(keyedContainer): 78 80 if keyedContainer.children.isEmpty { 79 81 if !key.isEmpty { 80 - dictionaryRepresentation[key] = String() 82 + queryItems.append(URLQueryItem(name: key, value: String())) 81 83 } 82 84 } else { 83 85 for (subKey, childContainer) in keyedContainer.children { 84 86 let nextKey = "\(key)\(separator)\(subKey)" 85 - encode(childContainer, at: nextKey, into: &dictionaryRepresentation) 87 + encode(childContainer, at: nextKey, into: &queryItems) 86 88 } 87 89 } 88 90 case let .lowLevelEncoder(lowLevelEncoder): 89 91 guard let childContainer = lowLevelEncoder.container else { 90 92 preconditionFailure("Nothing was never encoded to nested low level encoder.") 91 93 } 92 - encode(childContainer, at: key, into: &dictionaryRepresentation) 94 + encode(childContainer, at: key, into: &queryItems) 93 95 case let .singleValue(singleValueContainer): 94 96 switch singleValueContainer.storage { 95 97 case let .container(childContainer): 96 - encode(childContainer, at: key, into: &dictionaryRepresentation) 98 + encode(childContainer, at: key, into: &queryItems) 97 99 case let .primitive(value): 98 - if let value { 99 - dictionaryRepresentation[key] = String(describing: value) 100 - } else if !key.isEmpty { 101 - dictionaryRepresentation.updateValue(nil, forKey: key) 100 + if !key.isEmpty { 101 + queryItems.append(URLQueryItem(name: key, value: value.map { String(describing: $0) })) 102 102 } 103 103 case .none: 104 104 preconditionFailure("Value was never encoded to single value container.") 105 105 } 106 106 case let .unkeyed(unkeyedContainer): 107 - for index in unkeyedContainer.children.indices { 108 - let childContainer = unkeyedContainer.children[index] 109 - let nextKey = "\(key)\(separator)\(index)" 110 - encode(childContainer, at: nextKey, into: &dictionaryRepresentation) 107 + for (index, childContainer) in unkeyedContainer.children.enumerated() { 108 + switch strategies.arrayStrategy { 109 + case .indexed: 110 + let nextKey = "\(key)\(separator)\(index)" 111 + encode(childContainer, at: nextKey, into: &queryItems) 112 + case .repeated: 113 + encode(childContainer, at: key, into: &queryItems) 114 + } 111 115 } 112 116 } 113 117 } ··· 125 129 public func encode(_ value: some Encodable) throws -> [URLQueryItem] { 126 130 let container = try EncodingContainer.encodeWithSpecialTreatment(value, at: [], using: strategies) 127 131 128 - var dictionaryRepresentation: [String: String?] = [:] 129 - 130 - encode(container, into: &dictionaryRepresentation) 131 - 132 - let queryItems = dictionaryRepresentation.map(URLQueryItem.init) 133 - 132 + var queryItems: [URLQueryItem] = [] 133 + 134 + encode(container, into: &queryItems) 135 + 134 136 if outputFormatting.contains(.sortedKeys) { 135 137 return queryItems.sorted { $0.name < $1.name } 136 138 } else {