Skip to content

Commit 05401d4

Browse files
committed
Add "type state" based wrapper around Logging.Logger to handle initialisation via setLogger
1 parent 262936a commit 05401d4

File tree

1 file changed

+82
-2
lines changed

1 file changed

+82
-2
lines changed

Sources/App/Core/Dependencies/LoggerClient.swift

Lines changed: 82 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,18 +14,31 @@
1414

1515
import Dependencies
1616
import DependenciesMacros
17+
import IssueReporting
18+
import Logging
19+
import Synchronization
1720

1821

1922
@DependencyClient
2023
struct LoggerClient {
21-
24+
var log: @Sendable (_ level: Logging.Logger.Level, Logging.Logger.Message) -> Void
25+
var setLogger: @Sendable (Logging.Logger) -> Void
2226
}
2327

2428

2529
extension LoggerClient: DependencyKey {
2630
static var liveValue: Self {
27-
.init()
31+
.init(
32+
log: { level, message in
33+
_logger.withLock { $0.log(level: level, message) }
34+
},
35+
setLogger: { logger in
36+
_logger.withLock { $0.setLogger(logger) }
37+
}
38+
)
2839
}
40+
41+
private static let _logger = Mutex(Logger.uninitialized(.init()))
2942
}
3043

3144

@@ -40,3 +53,70 @@ extension DependencyValues {
4053
set { self[LoggerClient.self] = newValue }
4154
}
4255
}
56+
57+
58+
// Modeled after https://swiftology.io/articles/typestate/
59+
60+
extension LoggerClient {
61+
62+
private enum Logger: ~Copyable {
63+
case uninitialized(_UninitializedLogger)
64+
case initialized(_InitializedLogger)
65+
66+
mutating func setLogger(_ logger: Logging.Logger) {
67+
self = .initialized(.init(logger: logger))
68+
}
69+
70+
func log(
71+
level: Logging.Logger.Level,
72+
_ message: @autoclosure () -> Logging.Logger.Message,
73+
metadata: @autoclosure () -> Logging.Logger.Metadata? = nil,
74+
source: @autoclosure () -> String? = nil,
75+
file: String = #fileID,
76+
function: String = #function,
77+
line: UInt = #line
78+
) {
79+
switch self {
80+
case .uninitialized:
81+
break
82+
case let .initialized(logger):
83+
logger.log(level: level,
84+
message(),
85+
metadata: metadata(),
86+
source: source(),
87+
file: file,
88+
function: function,
89+
line: line)
90+
}
91+
}
92+
}
93+
94+
}
95+
96+
97+
private struct _UninitializedLogger: ~Copyable {
98+
consuming func setLogger(_ logger: Logging.Logger) -> _InitializedLogger {
99+
.init(logger: logger)
100+
}
101+
}
102+
103+
private struct _InitializedLogger: ~Copyable {
104+
var logger: Logging.Logger
105+
106+
init(logger: Logging.Logger) {
107+
self.logger = logger
108+
}
109+
110+
func log(
111+
level: Logging.Logger.Level,
112+
_ message: @autoclosure () -> Logging.Logger.Message,
113+
metadata: @autoclosure () -> Logging.Logger.Metadata? = nil,
114+
source: @autoclosure () -> String? = nil,
115+
file: String = #fileID,
116+
function: String = #function,
117+
line: UInt = #line
118+
) {
119+
logger.log(level: level, message(), metadata: metadata(), source: source(), file: file, function: function, line: line)
120+
}
121+
}
122+

0 commit comments

Comments
 (0)