Skip to content

Commit dedeaa9

Browse files
committed
refactoring of logging, async plugin prepare
1 parent ac73940 commit dedeaa9

File tree

7 files changed

+117
-60
lines changed

7 files changed

+117
-60
lines changed

Source/Plugin/Plugins.Basic.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,10 @@ public extension Plugins {
2828
#endif
2929

3030
/// The plugin that adds the basic authentication token to the header.
31-
static func AuthBasic(with tokenProvider: @escaping AuthBasicTokenProvider) -> Plugin {
31+
static func AuthBasic(overrideExisting: Bool = true, with tokenProvider: @escaping AuthBasicTokenProvider) -> Plugin {
3232
return TokenPlugin(id: "AuthBasic",
3333
priority: .authBasic,
34-
type: .header(.set("Authorization")),
34+
type: .header(overrideExisting ? .set("Authorization") : .trySet("Authorization")),
3535
tokenProvider: {
3636
return tokenProvider().map { token in
3737
let token = Data("\(token.username):\(token.password)".utf8).base64EncodedString()

Source/Plugin/Plugins.Bearer.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,12 @@ import Foundation
22

33
public extension Plugins {
44
/// The plugin that adds the `bearer` authentication token to the header.
5-
static func AuthBearer(with tokenProvider: @escaping TokenProvider) -> Plugin {
5+
static func AuthBearer(overrideExisting: Bool = true, with tokenProvider: @escaping TokenProvider) -> Plugin {
66
return TokenPlugin(id: "AuthBearer",
77
priority: .authBearer,
8-
type: .header(.set("Authorization")),
8+
type: .header(overrideExisting ? .set("Authorization") : .trySet("Authorization")),
99
tokenProvider: {
10-
return tokenProvider().map { token in
10+
return await tokenProvider().map { token in
1111
return "Bearer " + token
1212
}
1313
})

Source/Plugin/Plugins.Log.swift

Lines changed: 64 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -177,23 +177,32 @@ public extension Plugins.Log {
177177
/// The `curl` component of the log.
178178
struct Component: Hashable, RawRepresentable, ExpressibleByStringLiteral, CustomDebugStringConvertible, SmartSendable {
179179
public let rawValue: String
180+
/// The value is indicating the index the ``Component`` will iterated.
181+
public let sortingOrder: Int
180182

181183
public init(rawValue: String) {
182184
self.rawValue = rawValue
185+
self.sortingOrder = -1
183186
}
184187

185188
public init(stringLiteral value: String) {
186189
self.rawValue = value
190+
self.sortingOrder = -1
187191
}
188192

193+
init(rawValue: String, sortingOrder: Int) {
194+
self.rawValue = rawValue
195+
self.sortingOrder = sortingOrder
196+
}
197+
198+
public static let phase: Self = .init(rawValue: "phase", sortingOrder: 0)
199+
public static let url: Self = .init(rawValue: "url", sortingOrder: 10)
200+
public static let curl: Self = .init(rawValue: "curl", sortingOrder: 20)
201+
public static let error: Self = .init(rawValue: "error", sortingOrder: 30)
202+
public static let requestError: Self = .init(rawValue: "requestError", sortingOrder: 40)
203+
public static let body: Self = .init(rawValue: "body", sortingOrder: 50)
189204
public static let id: Self = "id"
190-
public static let url: Self = "url"
191-
public static let phase: Self = "phase"
192-
public static let curl: Self = "curl"
193205
public static let headers: Self = "headers"
194-
public static let error: Self = "error"
195-
public static let requestError: Self = "requestError"
196-
public static let body: Self = "body"
197206
public static let userInfo: Self = "userInfo"
198207
public static let parameters: Self = "parameters"
199208
public static let request: Self = "request"
@@ -246,28 +255,28 @@ public extension Plugins.Log {
246255
public static let all: Self = [.willSend, .didFinish, .didReceive, .wasCancelled]
247256
}
248257

249-
struct DataCollection: Sequence {
250-
public typealias Getter = () -> Any
258+
struct DataCollection: Sequence, CustomDebugStringConvertible {
259+
public typealias Getter<T> = () -> T
251260

252-
public let data: [Component: Getter]
261+
public let data: [Component: Getter<Any>]
253262

254-
public init(data: [Component: Getter] = [:]) {
263+
public init(data: [Component: Getter<Any>] = [:]) {
255264
self.data = data
256265
}
257266

258-
public func add(_ key: Component, _ value: @autoclosure @escaping Getter) -> Self {
267+
public func add(_ key: Component, _ value: @autoclosure @escaping Getter<Any>) -> Self {
259268
var data = data
260269
data[key] = value
261270
return .init(data: data)
262271
}
263272

264-
public func add(_ key: Component, _ value: @escaping Getter) -> Self {
273+
public func add(_ key: Component, _ value: @escaping Getter<Any>) -> Self {
265274
var data = data
266275
data[key] = value
267276
return .init(data: data)
268277
}
269278

270-
public func add(_ key: Component, if condition: Bool, _ value: @escaping Getter) -> Self {
279+
public func add(_ key: Component, if condition: Bool, _ value: @escaping Getter<Any>) -> Self {
271280
guard condition else {
272281
return self
273282
}
@@ -277,51 +286,75 @@ public extension Plugins.Log {
277286
return .init(data: data)
278287
}
279288

280-
public func getClosure(safe key: Component) -> (() -> Any)? {
289+
public func getClosure(safe key: Component) -> Getter<Any>? {
281290
return data[key]
282291
}
283292

284-
public func getClosure(_ key: Component) -> () -> Any {
293+
public func getClosure(_ key: Component) -> Getter<Any> {
285294
return data[key]!
286295
}
287296

288-
public func getAny(safe key: Component) -> Any? {
297+
public func getClosure<T>(safe key: Component, ofType: T.Type) -> Getter<T>? {
298+
if let value = data[key]?() as? T {
299+
return { [value] in
300+
return value
301+
}
302+
}
303+
return nil
304+
}
305+
306+
public func getClosure<T>(_ key: Component, ofType: T.Type) -> Getter<T> {
307+
return { [data] in
308+
return data[key]!() as! T
309+
}
310+
}
311+
312+
public func get(safe key: Component) -> Any? {
289313
return data[key]?()
290314
}
291315

292-
public func getAny(_ key: Component) -> Any {
316+
public func get(_ key: Component) -> Any {
293317
return data[key]!()
294318
}
295319

296-
public func get<T>(safe key: Component, ofType: T.Type = T.self) -> T? {
320+
public func get<T>(safe key: Component, ofType: T.Type) -> T? {
297321
return data[key]?() as? T
298322
}
299323

300-
public func get<T>(_ key: Component, ofType: T.Type = T.self) -> T {
324+
public func get<T>(_ key: Component, ofType: T.Type) -> T {
301325
return data[key]?() as! T
302326
}
303327

304-
public subscript<T>(safe key: Component) -> T? {
305-
return get(safe: key)
328+
public subscript<T>(safe key: Component, ofType type: T.Type) -> T? {
329+
return get(safe: key, ofType: type)
306330
}
307331

308-
public subscript<T>(_ key: Component) -> T {
309-
return get(key)
332+
public subscript<T>(_ key: Component, ofType type: T.Type) -> T {
333+
return get(key, ofType: type)
310334
}
311335

312336
public func makeIterator() -> Iterator {
313337
return .init(data: self)
314338
}
315339

316340
public struct Iterator: IteratorProtocol {
317-
public typealias Element = (key: Component, value: Getter)
341+
public typealias Element = (key: Component, value: Getter<Any>)
318342

319343
private let data: [Element]
320344
private var index: Int
321345

322346
init(data: DataCollection) {
323347
self.data = data.data.sorted(by: { a, b in
324-
return a.key.rawValue < b.key.rawValue
348+
switch (a.key.sortingOrder, b.key.sortingOrder) {
349+
case (-1, -1):
350+
return a.key.rawValue < b.key.rawValue
351+
case (-1, _):
352+
return false
353+
case (_, -1):
354+
return true
355+
default:
356+
return a.key.sortingOrder < b.key.sortingOrder
357+
}
325358
})
326359
self.index = 0
327360
}
@@ -336,5 +369,11 @@ public extension Plugins.Log {
336369
}
337370
}
338371
}
372+
373+
public var debugDescription: String {
374+
return data.mapValues {
375+
return $0()
376+
}.debugDescription
377+
}
339378
}
340379
}

Source/Plugin/Plugins.LogOS.swift

Lines changed: 30 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,15 @@ public extension Plugins {
88
/// A type that maps the ID and URL to a loggable strings.
99
///
1010
/// - Important: If returning `nil`, it will not be logged.
11-
typealias CurlOSMapper = @Sendable (_ id: String, _ url: () -> String) -> String?
11+
typealias CurlOSMapper = @Sendable (_ id: String, _ url: String) -> String?
1212

1313
/// Logger generator
1414
typealias LoggerGenerator = @Sendable (_ data: Plugins.Log.DataCollection) -> os.Logger
1515
#else
1616
/// A type that maps the ID and URL to a loggable strings.
1717
///
1818
/// - Important: If returning `nil`, it will not be logged.
19-
typealias CurlOSMapper = (_ id: String, _ url: () -> String) -> String?
19+
typealias CurlOSMapper = (_ id: String, _ url: String) -> String?
2020

2121
/// Logger generator
2222
typealias LoggerGenerator = (_ data: Plugins.Log.DataCollection) -> os.Logger
@@ -44,24 +44,18 @@ public extension Plugins {
4444
options: Plugins.Log.Options = .all,
4545
mapID: CurlOSMapper? = nil) -> Plugins.Log {
4646
return .init(id: Plugins.Log.makeHash(withAdditionalHash: "OS"),
47-
priority: priority) { data in
47+
priority: priority,
48+
options: options) { [mapID, shouldPrintBody] data in
4849
let logger = logger.get(data)
4950

50-
let id: String
51-
if let mapID {
52-
guard let uuid: UUID = data[safe: .id],
53-
let url: () -> String = data[safe: .url],
54-
let str = mapID(uuid.uuidString, url) else {
55-
logger.log(level: .error, "CurlOS: No ID mapping")
56-
return
57-
}
51+
let id: String?
52+
if let mapID,
53+
let uuid = data.get(safe: .id, ofType: UUID.self)?.uuidString,
54+
let url = data.get(safe: .url, ofType: String.self),
55+
let str = mapID(uuid, url) {
5856
id = str
5957
} else {
60-
guard let str: UUID = data[safe: .id] else {
61-
logger.log(level: .error, "CurlOS: No ID mapping")
62-
return
63-
}
64-
id = str.uuidString
58+
id = nil
6559
}
6660

6761
for (component, value) in data {
@@ -75,7 +69,15 @@ public extension Plugins {
7569
continue
7670
}
7771

78-
logger.log(level: component == .error ? .error : .info, "\(text) - \(id)")
72+
if component == .body, shouldPrintBody {
73+
#if DEBUG
74+
print(text)
75+
#endif
76+
} else if let id {
77+
logger.log(level: component == .error ? .error : .info, "\(text) - \(id)")
78+
} else {
79+
logger.log(level: component == .error ? .error : .info, "\(text)")
80+
}
7981
}
8082
}
8183
}
@@ -99,14 +101,19 @@ public extension Plugins.LoggerProvider {
99101
case .generator(let generator):
100102
return generator(data)
101103
case .identifiable:
102-
let url: String = data[.url]
103-
let uuid: UUID = data[.id]
104-
let path: String = ((try? AddressDetails(string: url))?.path).map { $0.isEmpty ? "< root >" : $0.joined(separator: "/") } ?? "< unknown path >"
105-
let id: String = uuid.uuidString.components(separatedBy: "-").first ?? uuid.uuidString
106-
let identity = path + " " + id
107-
104+
let url = data[.url, ofType: String.self]
105+
let uuid = data[.id, ofType: UUID.self].uuidString
106+
let identity = PluginsLogIdentity(uuid, url)
108107
return .init(subsystem: Bundle.main.bundleIdentifier ?? "SmartNetwork", category: "Plugins.LogOS.\(identity)")
109108
}
110109
}
111110
}
111+
112+
@inline(__always)
113+
public func PluginsLogIdentity(_ uuid: String, _ url: String) -> String {
114+
let path: String = ((try? AddressDetails(string: url))?.path).map { $0.isEmpty ? "< root >" : $0.joined(separator: "/") } ?? "< unknown path >"
115+
let id: String = uuid.components(separatedBy: "-").first ?? uuid
116+
let identity = path + " " + id
117+
return identity
118+
}
112119
#endif

Source/Plugin/Plugins.TokenPlugin.swift

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,19 @@ import Foundation
33
public extension Plugins {
44
#if swift(>=6.0)
55
/// The token provider.
6-
typealias TokenProvider = @Sendable () -> String?
6+
typealias TokenProvider = @Sendable () async -> String?
77
#else
88
/// The token provider.
9-
typealias TokenProvider = () -> String?
9+
typealias TokenProvider = () async -> String?
1010
#endif
1111

1212
/// The type of the plugin where the token will be applied.
1313
enum TokenType: SmartSendable {
1414
public enum Operation: SmartSendable {
1515
/// Set token to the request. The previous value will be rewriten.
1616
case set(String)
17+
/// Set token to the request only if no value for key.
18+
case trySet(String)
1719
/// Add token to the request. The new value will be added to the existing one.
1820
case add(String)
1921
}
@@ -45,13 +47,17 @@ public extension Plugins {
4547
}
4648

4749
public func prepare(parameters: Parameters, userInfo: UserInfo, request: inout URLRequestRepresentation, session: SmartURLSession) async {
48-
let value = tokenProvider()
50+
let value = await tokenProvider()
4951

5052
switch type {
5153
case .header(let operation):
5254
switch operation {
5355
case .set(let key):
5456
request.setValue(value, forHTTPHeaderField: key)
57+
case .trySet(let key):
58+
if request.value(forHTTPHeaderField: key) == nil {
59+
request.setValue(value, forHTTPHeaderField: key)
60+
}
5561
case .add(let key):
5662
if let value {
5763
request.addValue(value, forHTTPHeaderField: key)
@@ -65,6 +71,10 @@ public extension Plugins {
6571
case .set(let key):
6672
queryItems = queryItems.filter { $0.name != key }
6773
queryItems.append(URLQueryItem(name: key, value: value))
74+
case .trySet(let key):
75+
if !queryItems.contains(where: { $0.name == key }) {
76+
queryItems.append(URLQueryItem(name: key, value: value))
77+
}
6878
case .add(let key):
6979
queryItems.append(URLQueryItem(name: key, value: value))
7080
}

Source/Retrier/AnyErrorRetrier.swift renamed to Source/Retrier/ConnectionErrorRetrier.swift

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import Foundation
22

3-
public struct AnyErrorRetrier: SmartRetrier {
3+
public struct ConnectionErrorRetrier: SmartRetrier {
44
public let result: RetryResult
55
public let attemptsCount: Int
66

@@ -10,7 +10,8 @@ public struct AnyErrorRetrier: SmartRetrier {
1010
}
1111

1212
public func retryOrFinish(result: SmartResponse, address: Address, parameters: Parameters, userInfo: UserInfo) -> RetryResult {
13-
if userInfo.attemptsCount < attemptsCount {
13+
if case .connection = result.error?.requestError,
14+
userInfo.attemptsCount < attemptsCount {
1415
return .retry
1516
}
1617
return self.result

Source/Stub/HTTPStubCondition.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -223,8 +223,8 @@ private extension String {
223223
}
224224

225225
#if swift(>=6.0)
226-
extension HTTPStubCondition: @unchecked Sendable {
227-
public typealias TestClosure = (_ request: URLRequestRepresentation) -> Bool
226+
extension HTTPStubCondition: Sendable {
227+
public typealias TestClosure = @Sendable (_ request: URLRequestRepresentation) -> Bool
228228
}
229229
#else
230230
public extension HTTPStubCondition {

0 commit comments

Comments
 (0)