diff --git a/Sources/LazyContainers/FunctionalLazy.swift b/Sources/Lazy/FunctionalLazy.swift similarity index 96% rename from Sources/LazyContainers/FunctionalLazy.swift rename to Sources/Lazy/FunctionalLazy.swift index 11d2412..3ee7d99 100644 --- a/Sources/LazyContainers/FunctionalLazy.swift +++ b/Sources/Lazy/FunctionalLazy.swift @@ -73,6 +73,11 @@ public struct FunctionalLazy: LazyContainer { public var isInitialized: Bool { _guts.isInitialized } + public mutating func initializeNow() { + _guts.initializeNow() + } + + /// The actual functionality of `FunctionalLazy`, separated so that the semantics work out better @propertyWrapper @@ -114,6 +119,11 @@ public struct FunctionalLazy: LazyContainer { set { initializer = { newValue } } } + + func initializeNow() { + _ = initializer() + } + /// Indicates whether the value has indeed been initialized public var isInitialized: Bool { nil == semaphore } diff --git a/Sources/LazyContainers/Lazy.swift b/Sources/Lazy/Lazy.swift similarity index 88% rename from Sources/LazyContainers/Lazy.swift rename to Sources/Lazy/Lazy.swift index d3771af..2aa5650 100644 --- a/Sources/LazyContainers/Lazy.swift +++ b/Sources/Lazy/Lazy.swift @@ -19,11 +19,11 @@ public struct Lazy: LazyContainer { /// Privatizes the inner-workings of this functional lazy container @ValueReference - private var guts: ValueHolder + private var guts: ValueHolder /// Allows other initializers to have a shared point of initialization - private init(_guts: ValueReference>) { + private init(_guts: ValueReference) { self._guts = _guts } @@ -73,6 +73,11 @@ public struct Lazy: LazyContainer { /// Indicates whether the value has indeed been initialized public var isInitialized: Bool { _guts.wrappedValue.hasValue } + + + public mutating func initializeNow() { + guts.initializeNow() + } } @@ -120,6 +125,18 @@ public enum LazyContainerValueHolder { case .unset(initializer: _): return false } } + + + /// Immediately initializes the value held inside this value holder + /// + /// If this holder already contains a value, this does nothing + mutating func initializeNow() { + switch self { + case .hasValue(_): return + case .unset(let initializer): + self = .hasValue(value: initializer()) + } + } } @@ -128,7 +145,5 @@ public enum LazyContainerValueHolder { public extension LazyContainer { /// Takes care of keeping track of the state, value, and initializer as needed - /// - /// - Attention: This will change in version 5, to be an alias to `LazyContainerValueHolder` - typealias ValueHolder = LazyContainerValueHolder + typealias ValueHolder = LazyContainerValueHolder } diff --git a/Sources/LazyContainers/LazyContainer + Codable.swift b/Sources/Lazy/LazyContainer + Codable.swift similarity index 100% rename from Sources/LazyContainers/LazyContainer + Codable.swift rename to Sources/Lazy/LazyContainer + Codable.swift diff --git a/Sources/LazyContainers/LazyContainer + Equatable.swift b/Sources/Lazy/LazyContainer + Equatable.swift similarity index 100% rename from Sources/LazyContainers/LazyContainer + Equatable.swift rename to Sources/Lazy/LazyContainer + Equatable.swift diff --git a/Sources/LazyContainers/LazyContainer + Hashable.swift b/Sources/Lazy/LazyContainer + Hashable.swift similarity index 100% rename from Sources/LazyContainers/LazyContainer + Hashable.swift rename to Sources/Lazy/LazyContainer + Hashable.swift diff --git a/Sources/LazyContainers/LazyContainer.swift b/Sources/Lazy/LazyContainer.swift similarity index 88% rename from Sources/LazyContainers/LazyContainer.swift rename to Sources/Lazy/LazyContainer.swift index 9c845a7..109e311 100644 --- a/Sources/LazyContainers/LazyContainer.swift +++ b/Sources/Lazy/LazyContainer.swift @@ -34,6 +34,13 @@ public protocol LazyContainer { var isInitialized: Bool { get } + /// Immediately initializes the value held inside this lazy container + /// + /// If this holder already contains a value, this does nothing + // https://github.com/RougeWare/Swift-Lazy-Containers/issues/40 + mutating func initializeNow() + + /// Creates a lazy container that already contains an initialized value. /// /// This is useful when you need a uniform API (for instance, when implementing a protocol that requires a `Lazy`), @@ -68,7 +75,5 @@ public final class LazyContainerValueReference { public extension LazyContainer { /// Allows you to use reference semantics to hold a value inside a lazy container. - /// - /// - Attention: This will change in version 5, to be an alias to `LazyContainerValueReference` typealias ValueReference = LazyContainerValueReference } diff --git a/Sources/LazyContainers/ResettableLazy.swift b/Sources/Lazy/ResettableLazy.swift similarity index 90% rename from Sources/LazyContainers/ResettableLazy.swift rename to Sources/Lazy/ResettableLazy.swift index 06f7345..9a89677 100644 --- a/Sources/LazyContainers/ResettableLazy.swift +++ b/Sources/Lazy/ResettableLazy.swift @@ -20,11 +20,11 @@ public struct ResettableLazy: LazyContainer { /// Privatizes the inner-workings of this functional lazy container @ValueReference - private var guts: ResettableValueHolder + private var guts: ResettableValueHolder /// Allows other initializers to have a shared point of initialization - private init(_guts: ValueReference>) { + private init(_guts: ValueReference) { self._guts = _guts } @@ -80,6 +80,11 @@ public struct ResettableLazy: LazyContainer { public var isInitialized: Bool { _guts.wrappedValue.hasValue } + public mutating func initializeNow() { + guts.initializeNow() + } + + /// Resets this lazy structure back to its unset state. Next time a value is needed, it will be regenerated using /// the initializer given by the constructor public func clear() { @@ -147,6 +152,18 @@ public enum LazyContainerResettableValueHolder { case .unset(initializer: _): return false } } + + + /// Immediately initializes the value in this holder. + /// + /// If this holder already contains a value, this does nothing + public mutating func initializeNow() { + switch self { + case .hasValue(value: _, initializer: _): return + case .unset(let initializer): + self = .hasValue(value: initializer(), initializer: initializer) + } + } } @@ -156,7 +173,5 @@ public enum LazyContainerResettableValueHolder { public extension LazyContainer { /// Takes care of keeping track of the state, value, and initializer as needed - /// - /// - Attention: This will change in version 5, to be an alias to `LazyContainerResettableValueHolder` - typealias ResettableValueHolder = LazyContainerResettableValueHolder + typealias ResettableValueHolder = LazyContainerResettableValueHolder } diff --git a/Sources/LazyContainers/migration assistant.swift b/Sources/Lazy/migration assistant.swift similarity index 100% rename from Sources/LazyContainers/migration assistant.swift rename to Sources/Lazy/migration assistant.swift diff --git a/Tests/LazyContainersTests/GitHubIssue20Tests.swift b/Tests/LazyTests/GitHubIssue20Tests.swift similarity index 100% rename from Tests/LazyContainersTests/GitHubIssue20Tests.swift rename to Tests/LazyTests/GitHubIssue20Tests.swift diff --git a/Tests/LazyContainersTests/LazyContainer + Codable tests.swift b/Tests/LazyTests/LazyContainer + Codable tests.swift similarity index 100% rename from Tests/LazyContainersTests/LazyContainer + Codable tests.swift rename to Tests/LazyTests/LazyContainer + Codable tests.swift diff --git a/Tests/LazyContainersTests/LazyContainer + Equatable tests.swift b/Tests/LazyTests/LazyContainer + Equatable tests.swift similarity index 100% rename from Tests/LazyContainersTests/LazyContainer + Equatable tests.swift rename to Tests/LazyTests/LazyContainer + Equatable tests.swift diff --git a/Tests/LazyContainersTests/LazyContainer + Hashable tests.swift b/Tests/LazyTests/LazyContainer + Hashable tests.swift similarity index 100% rename from Tests/LazyContainersTests/LazyContainer + Hashable tests.swift rename to Tests/LazyTests/LazyContainer + Hashable tests.swift diff --git a/Tests/LazyContainersTests/LazyContainersTests.swift b/Tests/LazyTests/LazyContainersTests.swift similarity index 55% rename from Tests/LazyContainersTests/LazyContainersTests.swift rename to Tests/LazyTests/LazyContainersTests.swift index a3b3a39..56a9ab7 100644 --- a/Tests/LazyContainersTests/LazyContainersTests.swift +++ b/Tests/LazyTests/LazyContainersTests.swift @@ -144,6 +144,66 @@ struct LazyContainersTests { + // MARK: .initializeNow() + + // https://github.com/RougeWare/Swift-Lazy-Containers/issues/40 + + @Test + mutating func initializeNow_Lazy_traditional() { + #expect(false == lazyInitTraditionally.isInitialized) + lazyInitTraditionally.initializeNow() + #expect(true == lazyInitTraditionally.isInitialized) + #expect("lazy B" == lazyInitTraditionally.wrappedValue) + #expect(true == lazyInitTraditionally.isInitialized) + #expect("lazy B" == lazyInitTraditionally.wrappedValue) + #expect(true == lazyInitTraditionally.isInitialized) + + lazyInitTraditionally.wrappedValue = "Manual B" + + #expect(true == lazyInitTraditionally.isInitialized) + #expect("Manual B" == lazyInitTraditionally.wrappedValue) + #expect(true == lazyInitTraditionally.isInitialized) + #expect("Manual B" == lazyInitTraditionally.wrappedValue) + #expect(true == lazyInitTraditionally.isInitialized) + #expect("Manual B" == lazyInitTraditionally.wrappedValue) + #expect(true == lazyInitTraditionally.isInitialized) + } + + + @Test + mutating func initializeNow_Lazy_customInitWithSideEffect() { + #expect(sideEffectA == nil) + #expect(false == _lazyInitWithPropertyWrapperAndCustomInitializerWithSideEffect.isInitialized) + #expect(sideEffectA == nil) + _lazyInitWithPropertyWrapperAndCustomInitializerWithSideEffect.initializeNow() + #expect(true == _lazyInitWithPropertyWrapperAndCustomInitializerWithSideEffect.isInitialized) + #expect(sideEffectA == "Side effect A1") + #expect("lAzy" == lazyInitWithPropertyWrapperAndCustomInitializerWithSideEffect) + #expect(sideEffectA == "Side effect A1") + #expect(true == _lazyInitWithPropertyWrapperAndCustomInitializerWithSideEffect.isInitialized) + #expect(sideEffectA == "Side effect A1") + #expect("lAzy" == lazyInitWithPropertyWrapperAndCustomInitializerWithSideEffect) + #expect(sideEffectA == "Side effect A1") + #expect(true == _lazyInitWithPropertyWrapperAndCustomInitializerWithSideEffect.isInitialized) + #expect(sideEffectA == "Side effect A1") + + lazyInitWithPropertyWrapperAndCustomInitializerWithSideEffect = "MAnual" + + #expect(sideEffectA == "Side effect A1") + #expect(true == _lazyInitWithPropertyWrapperAndCustomInitializerWithSideEffect.isInitialized) + #expect(sideEffectA == "Side effect A1") + #expect("MAnual" == lazyInitWithPropertyWrapperAndCustomInitializerWithSideEffect) + #expect(sideEffectA == "Side effect A1") + #expect(true == _lazyInitWithPropertyWrapperAndCustomInitializerWithSideEffect.isInitialized) + #expect(sideEffectA == "Side effect A1") + #expect("MAnual" == lazyInitWithPropertyWrapperAndCustomInitializerWithSideEffect) + #expect(sideEffectA == "Side effect A1") + #expect(true == _lazyInitWithPropertyWrapperAndCustomInitializerWithSideEffect.isInitialized) + #expect(sideEffectA == "Side effect A1") + } + + + // MARK: - `ResettableLazy` @Test @@ -209,6 +269,73 @@ struct LazyContainersTests { #expect(true == resettableLazyInitTraditionally.isInitialized) } + // MARK: .initializeNow() + + // https://github.com/RougeWare/Swift-Lazy-Containers/issues/40 + + @Test + mutating func initializeNow_ResettableLazy_propertyWrapper() { + #expect(false == _resettableLazyInitWithPropertyWrapper.isInitialized) + _resettableLazyInitWithPropertyWrapper.initializeNow() + #expect(true == _resettableLazyInitWithPropertyWrapper.isInitialized) + #expect("lazy C" == resettableLazyInitWithPropertyWrapper) + #expect(true == _resettableLazyInitWithPropertyWrapper.isInitialized) + #expect("lazy C" == resettableLazyInitWithPropertyWrapper) + #expect(true == _resettableLazyInitWithPropertyWrapper.isInitialized) + + resettableLazyInitWithPropertyWrapper = "Manual C" + + #expect(true == _resettableLazyInitWithPropertyWrapper.isInitialized) + #expect("Manual C" == resettableLazyInitWithPropertyWrapper) + #expect(true == _resettableLazyInitWithPropertyWrapper.isInitialized) + #expect("Manual C" == resettableLazyInitWithPropertyWrapper) + #expect(true == _resettableLazyInitWithPropertyWrapper.isInitialized) + #expect("Manual C" == resettableLazyInitWithPropertyWrapper) + #expect(true == _resettableLazyInitWithPropertyWrapper.isInitialized) + + _resettableLazyInitWithPropertyWrapper.clear() + + #expect(false == _resettableLazyInitWithPropertyWrapper.isInitialized) + #expect("lazy C" == resettableLazyInitWithPropertyWrapper) + #expect(true == _resettableLazyInitWithPropertyWrapper.isInitialized) + #expect("lazy C" == resettableLazyInitWithPropertyWrapper) + #expect(true == _resettableLazyInitWithPropertyWrapper.isInitialized) + #expect("lazy C" == resettableLazyInitWithPropertyWrapper) + #expect(true == _resettableLazyInitWithPropertyWrapper.isInitialized) + } + + + @Test + mutating func initializeNow_ResettableLazy_traditional() { + #expect(false == resettableLazyInitTraditionally.isInitialized) + resettableLazyInitTraditionally.initializeNow() + #expect(true == resettableLazyInitTraditionally.isInitialized) + #expect("lazy D" == resettableLazyInitTraditionally.wrappedValue) + #expect(true == resettableLazyInitTraditionally.isInitialized) + #expect("lazy D" == resettableLazyInitTraditionally.wrappedValue) + #expect(true == resettableLazyInitTraditionally.isInitialized) + + resettableLazyInitTraditionally.wrappedValue = "Manual D" + + #expect(true == resettableLazyInitTraditionally.isInitialized) + #expect("Manual D" == resettableLazyInitTraditionally.wrappedValue) + #expect(true == resettableLazyInitTraditionally.isInitialized) + #expect("Manual D" == resettableLazyInitTraditionally.wrappedValue) + #expect(true == resettableLazyInitTraditionally.isInitialized) + #expect("Manual D" == resettableLazyInitTraditionally.wrappedValue) + #expect(true == resettableLazyInitTraditionally.isInitialized) + + resettableLazyInitTraditionally.clear() + + #expect(false == resettableLazyInitTraditionally.isInitialized) + #expect("lazy D" == resettableLazyInitTraditionally.wrappedValue) + #expect(true == resettableLazyInitTraditionally.isInitialized) + #expect("lazy D" == resettableLazyInitTraditionally.wrappedValue) + #expect(true == resettableLazyInitTraditionally.isInitialized) + #expect("lazy D" == resettableLazyInitTraditionally.wrappedValue) + #expect(true == resettableLazyInitTraditionally.isInitialized) + } + // MARK: - `FuctionalLazy` @@ -255,4 +382,52 @@ struct LazyContainersTests { #expect("Manual F" == functionalLazyInitTraditionally.wrappedValue) #expect(true == functionalLazyInitTraditionally.isInitialized) } + + + // MARK: .initializeNow() + + // https://github.com/RougeWare/Swift-Lazy-Containers/issues/40 + + @Test + mutating func initializeNow_FunctionalLazy_propertyWrapper() { + #expect(false == _functionalLazyInitWithPropertyWrapper.isInitialized) + _functionalLazyInitWithPropertyWrapper.initializeNow() + #expect(true == _functionalLazyInitWithPropertyWrapper.isInitialized) + #expect("lazy E" == functionalLazyInitWithPropertyWrapper) + #expect(true == _functionalLazyInitWithPropertyWrapper.isInitialized) + #expect("lazy E" == functionalLazyInitWithPropertyWrapper) + #expect(true == _functionalLazyInitWithPropertyWrapper.isInitialized) + + functionalLazyInitWithPropertyWrapper = "Manual E" + + #expect(true == _functionalLazyInitWithPropertyWrapper.isInitialized) + #expect("Manual E" == functionalLazyInitWithPropertyWrapper) + #expect(true == _functionalLazyInitWithPropertyWrapper.isInitialized) + #expect("Manual E" == functionalLazyInitWithPropertyWrapper) + #expect(true == _functionalLazyInitWithPropertyWrapper.isInitialized) + #expect("Manual E" == functionalLazyInitWithPropertyWrapper) + #expect(true == _functionalLazyInitWithPropertyWrapper.isInitialized) + } + + + @Test + mutating func initializeNow_FunctionalLazy_traditional() { + #expect(false == functionalLazyInitTraditionally.isInitialized) + functionalLazyInitTraditionally.initializeNow() + #expect(true == functionalLazyInitTraditionally.isInitialized) + #expect("lazy F" == functionalLazyInitTraditionally.wrappedValue) + #expect(true == functionalLazyInitTraditionally.isInitialized) + #expect("lazy F" == functionalLazyInitTraditionally.wrappedValue) + #expect(true == functionalLazyInitTraditionally.isInitialized) + + functionalLazyInitTraditionally.wrappedValue = "Manual F" + + #expect(true == functionalLazyInitTraditionally.isInitialized) + #expect("Manual F" == functionalLazyInitTraditionally.wrappedValue) + #expect(true == functionalLazyInitTraditionally.isInitialized) + #expect("Manual F" == functionalLazyInitTraditionally.wrappedValue) + #expect(true == functionalLazyInitTraditionally.isInitialized) + #expect("Manual F" == functionalLazyInitTraditionally.wrappedValue) + #expect(true == functionalLazyInitTraditionally.isInitialized) + } }