1414
1515import Dependencies
1616import DependenciesMacros
17+ import IssueReporting
18+ import Logging
19+ import Synchronization
1720
1821
1922@DependencyClient
2023struct LoggerClient {
21-
24+ var log : @Sendable ( _ level: Logging . Logger . Level , Logging . Logger . Message ) -> Void
25+ var setLogger : @Sendable ( Logging . Logger ) -> Void
2226}
2327
2428
2529extension 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