1+ private actor MemoizedClosure < T> {
2+ var closure : @Sendable ( ) async throws -> T
3+ var cache : T ?
4+
5+ init ( _ closure: @escaping @Sendable ( ) async throws -> T ) {
6+ self . closure = closure
7+ }
8+
9+ func set( _ cache: T ) -> T {
10+ self . cache = cache
11+ return cache
12+ }
13+
14+ func call( _ withoutCaching: Bool ) async throws -> T {
15+ if withoutCaching {
16+ return try await closure ( )
17+ }
18+ if let cache {
19+ return cache
20+ } else {
21+ return set ( try await closure ( ) )
22+ }
23+ }
24+ }
25+
126// Memoizes the given closure, only calling the passed
227// closure once; even if repeat calls to the returned closure
3- private func memoizedClosure< T> ( _ closure: @escaping ( ) async throws -> T ) -> ( Bool ) async throws -> T {
4- var cache : T ?
28+ private func memoizedClosure< T> ( _ closure: @escaping @ Sendable ( ) async throws -> T ) -> @ Sendable ( Bool) async throws -> T {
29+ let memoized = MemoizedClosure ( closure )
530 return { withoutCaching in
6- if withoutCaching || cache == nil {
7- cache = try await closure ( )
8- }
9- return cache!
31+ try await memoized. call ( withoutCaching)
1032 }
1133}
1234
@@ -21,8 +43,8 @@ private func memoizedClosure<T>(_ closure: @escaping () async throws -> T) -> (B
2143///
2244/// This provides a common consumable API for matchers to utilize to allow
2345/// Nimble to change internals to how the captured closure is managed.
24- public struct AsyncExpression < Value> {
25- internal let _expression : ( Bool ) async throws -> Value ?
46+ public struct AsyncExpression < Value> : Sendable {
47+ internal let _expression : @ Sendable ( Bool) async throws -> Value ?
2648 internal let _withoutCaching : Bool
2749 public let location : SourceLocation
2850 public let isClosure : Bool
@@ -38,7 +60,7 @@ public struct AsyncExpression<Value> {
3860 /// requires an explicit closure. This gives Nimble
3961 /// flexibility if @autoclosure behavior changes between
4062 /// Swift versions. Nimble internals always sets this true.
41- public init ( expression: @escaping ( ) async throws -> Value ? , location: SourceLocation , isClosure: Bool = true ) {
63+ public init ( expression: @escaping @ Sendable ( ) async throws -> Value ? , location: SourceLocation , isClosure: Bool = true ) {
4264 self . _expression = memoizedClosure ( expression)
4365 self . location = location
4466 self . _withoutCaching = false
@@ -59,7 +81,7 @@ public struct AsyncExpression<Value> {
5981 /// requires an explicit closure. This gives Nimble
6082 /// flexibility if @autoclosure behavior changes between
6183 /// Swift versions. Nimble internals always sets this true.
62- public init ( memoizedExpression: @escaping ( Bool ) async throws -> Value ? , location: SourceLocation , withoutCaching: Bool , isClosure: Bool = true ) {
84+ public init ( memoizedExpression: @escaping @ Sendable ( Bool) async throws -> Value ? , location: SourceLocation , withoutCaching: Bool , isClosure: Bool = true ) {
6385 self . _expression = memoizedExpression
6486 self . location = location
6587 self . _withoutCaching = withoutCaching
@@ -90,15 +112,15 @@ public struct AsyncExpression<Value> {
90112 ///
91113 /// - Parameter block: The block that can cast the current Expression value to a
92114 /// new type.
93- public func cast< U> ( _ block: @escaping ( Value ? ) throws -> U ? ) -> AsyncExpression < U > {
115+ public func cast< U> ( _ block: @escaping @ Sendable ( Value? ) throws -> U ? ) -> AsyncExpression < U > {
94116 AsyncExpression < U > (
95117 expression: ( { try await block ( self . evaluate ( ) ) } ) ,
96118 location: self . location,
97119 isClosure: self . isClosure
98120 )
99121 }
100122
101- public func cast< U> ( _ block: @escaping ( Value ? ) async throws -> U ? ) -> AsyncExpression < U > {
123+ public func cast< U> ( _ block: @escaping @ Sendable ( Value? ) async throws -> U ? ) -> AsyncExpression < U > {
102124 AsyncExpression < U > (
103125 expression: ( { try await block ( self . evaluate ( ) ) } ) ,
104126 location: self . location,
0 commit comments