Skip to content

Commit

Permalink
Extended debug functionality to include more methods captured by the …
Browse files Browse the repository at this point in the history
…logging. Added compactMap(mapping:) method to ValueOnlySubscriber.
  • Loading branch information
jakehawken committed Jun 8, 2022
1 parent d959510 commit 994bc58
Show file tree
Hide file tree
Showing 6 changed files with 154 additions and 49 deletions.
23 changes: 22 additions & 1 deletion Sources/Propagate/PublisherAndSubscriber/PropagateDebug.swift
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ internal func safePrint(_ message: String, logType: LogType, debugPair: DebugPai
}
var output = "<>DEBUG: "
if pair.message.count > 0 {
output += "\"\(pair.message)\" - "
output += "\(pair.message) - "
}
output += message
print(output)
Expand Down Expand Up @@ -82,3 +82,24 @@ extension Subscriber: CustomStringConvertible {
}

}

// MARK: - interface

public protocol PropagateDebuggable {
/// This method puts the type into a debug state where various events
/// (determined by the `DebugLogLevel`) are printed to the console.
///
/// - Parameter logLevel: The log level to which the events need to be filtered.
/// e.g. if all you care about is creation and release from memory of the type,
/// you would call:
/// `.debug(logLevel: .lifeCycleOnly, "When is this being released?")`
///
/// - Parameter additionalMessage: Any additional message you would like
/// included in the log, for example specific information about the given
/// type, e.g.
/// `.debug(logLevel: .operatorsOnly, "Mutations on the name stream.")`
///
/// - returns: The type itself, as a `@discardableResult`, to allow for seamless chaining.
@available(*, message: "This method is intended for debug use only. It is highly recommended that you not check in code calling this method.")
@discardableResult func debug(logLevel: DebugLogLevel, _ additionalMessage: String) -> Self
}
31 changes: 15 additions & 16 deletions Sources/Propagate/PublisherAndSubscriber/Publisher.swift
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ public class Publisher<T, E: Error> {
/// Emits a new `StreamState`. If this publisher has previously been
/// cancelled, this method will be a no-op.
internal func publishNewState(_ state: State) {
safePrint("Publishing state \(state) from \(self)", logType: .pubSub, debugPair: debugPair)
safePrint("Publishing state \(state). -- \(self)", logType: .pubSub, debugPair: debugPair)
lockQueue.async {
if self.isCancelled {
return
Expand All @@ -52,23 +52,16 @@ public class Publisher<T, E: Error> {
Any changes made to this function's implementation will
also need to be made to the same method on StatefulPublisher.
*/
let canceller = Canceller<T,E> { [weak self] in
let newSub = Subscriber(canceller: .init { [weak self] in
self?.removeSubscriber($0)
}
let newSub = Subscriber(canceller: canceller)
})
lockQueue.async { [weak self] in
self?.subscribers.insert(newSub)
}
safePrint("Generating new subscriber: \(newSub) from \(self)", logType: .lifeCycle, debugPair: debugPair)
return newSub
}

@available(*, message: "This method is intended for debug use only. It is highly recommended that you not check in code calling this method.")
@discardableResult public func debug(logLevel: DebugLogLevel = .all, _ additionalMessage: String = "") -> Self {
self.debugPair = (logLevel, additionalMessage)
return self
}

}

// MARK: debugging
Expand Down Expand Up @@ -124,6 +117,15 @@ public extension Publisher {

}

extension Publisher: PropagateDebuggable {

@discardableResult public func debug(logLevel: DebugLogLevel = .all, _ additionalMessage: String = "") -> Self {
self.debugPair = (logLevel, additionalMessage)
return self
}

}

// MARK: - private / helpers

private extension Publisher {
Expand Down Expand Up @@ -217,19 +219,16 @@ public class StatefulPublisher<T,E: Error>: Publisher<T, E> {
Any changes made to this function's implementation will
also need to be made to the same method on Publisher.
*/
let canceller = Canceller<T,E> { [weak self] in
let newSub = OnSubscribeCallbackSubscriber(canceller: .init { [weak self] in
self?.removeSubscriber($0)
}

// These two lines should be the only difference between this and the super.
let newSub = OnSubscribeCallbackSubscriber(canceller: canceller)
})
newSub.lastStateCallback = { [weak self] in self?.lastState }

lockQueue.async { [weak self] in
self?.subscribers.insert(newSub)
}
safePrint(
"Generating new subscriber: \(newSub) from \(self)",
"Generating new subscriber. -- \(self) --> \(newSub)",
logType: .lifeCycle,
debugPair: debugPair
)
Expand Down
17 changes: 17 additions & 0 deletions Sources/Propagate/PublisherAndSubscriber/Subscriber+Filter.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,12 @@ public extension Subscriber {
publisher.publishNewState(state)
}

safePrint(
"Filtering \(self)",
logType: .operators,
debugPair: debugPair
)

return publisher.subscriber()
.onCancelled {
_ = self // Capturing self to keep subscriber alive for easier chaining.
Expand Down Expand Up @@ -88,6 +94,11 @@ public extension Subscriber where T: Equatable {
}
}

safePrint(
"Removing contiguous duplicates values from \(self).",
logType: .operators,
debugPair: debugPair
)
return publisher.subscriber()
.onCancelled {
_ = self // Capturing self to keep subscriber alive for easier chaining.
Expand Down Expand Up @@ -123,6 +134,12 @@ public extension Subscriber where T: Equatable, E: Equatable {
}
}

safePrint(
"Removing contiguous duplicates values from \(self).",
logType: .operators,
debugPair: debugPair
)

return publisher.subscriber()
.onCancelled {
_ = self // Capturing self to keep subscriber alive for easier chaining.
Expand Down
30 changes: 13 additions & 17 deletions Sources/Propagate/PublisherAndSubscriber/Subscriber+Map.swift
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ public extension Subscriber {
let newSubscriber = newPublisher.subscriber()

safePrint(
"Mapping \(self) to \(newSubscriber)",
"Mapping states from StreamState<\(T.self),\(E.self)> to StreamState<\(NewT.self),\(NewE.self)>",
logType: .operators,
debugPair: debugPair
)
Expand All @@ -38,17 +38,15 @@ public extension Subscriber {
/// this subscriber to `.data` values of a different type on a new subscriber.
/// Other states (`.error` and `.cancelled`) pass through like normal.
func mapValues<NewT>(_ transform: @escaping (T) -> NewT) -> Subscriber<NewT, E> {
return mapStates { [weak self] oldState in
safePrint(
"Mapping values from \(T.self) to \(NewT.self)",
logType: .operators,
debugPair: debugPair
)
return mapStates { oldState in
switch oldState {
case .data(let data):
let transformed = transform(data)
if let pair = self?.debugPair {
safePrint(
"Mapped \(T.self)(\(oldState)) to \(NewT.self)(\(transformed))",
logType: .operators,
debugPair: pair
)
}
return .data(transformed)
case .error(let error):
return .error(error)
Expand Down Expand Up @@ -76,19 +74,17 @@ public extension Subscriber {
/// this subscriber to `.error` errors of a different type on a new subscriber.
/// Other states (`.data` and `.cancelled`) pass through like normal.
func mapErrors<NewE: Error>(_ transform: @escaping (E) -> NewE) -> Subscriber<T, NewE> {
return mapStates { [weak self] oldState in
safePrint(
"Mapping errors from \(E.self) to \(NewE.self).",
logType: .operators,
debugPair: debugPair
)
return mapStates { oldState in
switch oldState {
case .data(let data):
return .data(data)
case .error(let error):
let transformed = transform(error)
if let pair = self?.debugPair {
safePrint(
"Mapped \(E.self)(\(oldState)) to \(NewE.self)(\(transformed))",
logType: .operators,
debugPair: pair
)
}
return .error(transformed)
case .cancelled:
return .cancelled
Expand Down
12 changes: 10 additions & 2 deletions Sources/Propagate/PublisherAndSubscriber/Subscriber.swift
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,10 @@ public class Subscriber<T, E: Error> {
return self
}

@available(*, message: "This method is intended for debug use only. It is highly recommended that you not check in code calling this method.")
}

extension Subscriber: PropagateDebuggable {

@discardableResult public func debug(logLevel: DebugLogLevel = .all, _ additionalMessage: String = "") -> Self {
self.debugPair = (logLevel, additionalMessage)
return self
Expand Down Expand Up @@ -82,6 +85,11 @@ internal extension Subscriber {
/// its publisher. This will result in this subscriber
/// immediately receiving a `.cancelled` signal.
func cancel() {
safePrint(
"Cancelling \(self)...",
logType: .lifeCycle,
debugPair: debugPair
)
canceller.cancel(for: self)
receive(.cancelled)
}
Expand All @@ -97,7 +105,7 @@ private extension Subscriber {
return
}
safePrint(
"\(self) received \(state)",
"Received \(state). -- \(self)",
logType: .pubSub,
debugPair: debugPair
)
Expand Down
Loading

0 comments on commit 994bc58

Please sign in to comment.