-
-
Notifications
You must be signed in to change notification settings - Fork 14
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Unify @dependency property and introduce optional dependencies
- Loading branch information
Showing
20 changed files
with
450 additions
and
280 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
// | ||
// This source file is part of the Stanford Spezi open-source project | ||
// | ||
// SPDX-FileCopyrightText: 2023 Stanford University and the project authors (see CONTRIBUTORS.md) | ||
// | ||
// SPDX-License-Identifier: MIT | ||
// | ||
|
||
|
||
/// A result builder to build a ``DependencyCollection``. | ||
@resultBuilder | ||
public enum DependencyBuilder { | ||
/// An auto-closure expression, providing the default dependency value, building the ``DependencyCollection``. | ||
public static func buildExpression<M: Module>(_ expression: @escaping @autoclosure () -> M) -> DependencyCollection { | ||
DependencyCollection(DependencyContext(defaultValue: expression)) | ||
} | ||
|
||
/// Build a block of ``DependencyCollection``s. | ||
public static func buildBlock(_ components: DependencyCollection...) -> DependencyCollection { | ||
buildArray(components) | ||
} | ||
|
||
/// Build the first block of an conditional ``DependencyCollection`` component. | ||
public static func buildEither(first component: DependencyCollection) -> DependencyCollection { | ||
component | ||
} | ||
|
||
/// Build the second block of an conditional ``DependencyCollection`` component. | ||
public static func buildEither(second component: DependencyCollection) -> DependencyCollection { | ||
component | ||
} | ||
|
||
/// Build an optional ``DependencyCollection`` component. | ||
public static func buildOptional(_ component: DependencyCollection?) -> DependencyCollection { | ||
component ?? DependencyCollection() | ||
} | ||
|
||
/// Build an ``DependencyCollection`` component with limited availability. | ||
public static func buildLimitedAvailability(_ component: DependencyCollection) -> DependencyCollection { | ||
component | ||
} | ||
|
||
/// Build an array of ``DependencyCollection`` components. | ||
public static func buildArray(_ components: [DependencyCollection]) -> DependencyCollection { | ||
DependencyCollection(components.reduce(into: []) { result, component in | ||
result.append(contentsOf: component.entries) | ||
}) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
// | ||
// This source file is part of the Stanford Spezi open-source project | ||
// | ||
// SPDX-FileCopyrightText: 2023 Stanford University and the project authors (see CONTRIBUTORS.md) | ||
// | ||
// SPDX-License-Identifier: MIT | ||
// | ||
|
||
|
||
/// A collection of dependency declarations. | ||
public struct DependencyCollection: DependencyDeclaration { | ||
let entries: [AnyDependencyContext] | ||
|
||
|
||
init(_ entries: [AnyDependencyContext]) { | ||
self.entries = entries | ||
} | ||
|
||
init(_ entries: AnyDependencyContext...) { | ||
self.init(entries) | ||
} | ||
|
||
|
||
func collect(into dependencyManager: DependencyManager) { | ||
for entry in entries { | ||
entry.collect(into: dependencyManager) | ||
} | ||
} | ||
|
||
func inject(from dependencyManager: DependencyManager) { | ||
for entry in entries { | ||
entry.inject(from: dependencyManager) | ||
} | ||
} | ||
|
||
private func singleDependencyContext() -> AnyDependencyContext { | ||
guard let dependency = entries.first else { | ||
preconditionFailure("DependencyCollection unexpectedly empty!") | ||
} | ||
precondition(entries.count == 1, "Expected exactly one element in the dependency collection!") | ||
return dependency | ||
} | ||
|
||
func singleDependencyRetrieval<M>(for module: M.Type = M.self) -> M { | ||
singleDependencyContext().retrieve(dependency: M.self) | ||
} | ||
|
||
func singleOptionalDependencyRetrieval<M>(for module: M.Type = M.self) -> M? { | ||
singleDependencyContext().retrieveOptional(dependency: M.self) | ||
} | ||
|
||
func retrieveModules() -> [any Module] { | ||
entries.map { dependency in | ||
dependency.retrieve(dependency: (any Module).self) | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
// | ||
// This source file is part of the Stanford Spezi open-source project | ||
// | ||
// SPDX-FileCopyrightText: 2023 Stanford University and the project authors (see CONTRIBUTORS.md) | ||
// | ||
// SPDX-License-Identifier: MIT | ||
// | ||
|
||
import XCTRuntimeAssertions | ||
|
||
|
||
protocol AnyDependencyContext: DependencyDeclaration { | ||
func retrieve<M>(dependency: M.Type) -> M | ||
|
||
func retrieveOptional<M>(dependency: M.Type) -> M? | ||
} | ||
|
||
|
||
class DependencyContext<Dependency: Module>: AnyDependencyContext { | ||
let defaultValue: (() -> Dependency)? | ||
private var injectedDependency: Dependency? | ||
|
||
init(for type: Dependency.Type = Dependency.self, defaultValue: (() -> Dependency)? = nil) { | ||
self.defaultValue = defaultValue | ||
} | ||
|
||
func collect(into dependencyManager: DependencyManager) { | ||
dependencyManager.require(Dependency.self, defaultValue: defaultValue) | ||
} | ||
|
||
func inject(from dependencyManager: DependencyManager) { | ||
precondition(injectedDependency == nil, "Dependency of type \(Dependency.self) is already injected!") | ||
injectedDependency = dependencyManager.retrieve(optional: defaultValue == nil) | ||
} | ||
|
||
func retrieve<M>(dependency: M.Type) -> M { | ||
guard let injectedDependency else { | ||
preconditionFailure( | ||
""" | ||
A `@Dependency` was accessed before the dependency was activated. \ | ||
Only access dependencies once the module has been configured and the Spezi initialization is complete. | ||
""" | ||
) | ||
} | ||
guard let dependency = injectedDependency as? M else { | ||
preconditionFailure("A injected dependency of type \(type(of: injectedDependency)) didn't match the expected type \(M.self)!") | ||
} | ||
return dependency | ||
} | ||
|
||
func retrieveOptional<M>(dependency: M.Type) -> M? { | ||
guard let dependency = injectedDependency as? M? else { | ||
preconditionFailure("A injected dependency of type \(type(of: injectedDependency)) didn't match the expected type \(M?.self)!") | ||
} | ||
return dependency | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
// | ||
// This source file is part of the Stanford Spezi open-source project | ||
// | ||
// SPDX-FileCopyrightText: 2023 Stanford University and the project authors (see CONTRIBUTORS.md) | ||
// | ||
// SPDX-License-Identifier: MIT | ||
// | ||
|
||
|
||
/// Provides mechanism to communicate dependency requirements. | ||
/// | ||
/// This protocol allows to communicate dependency requirements of a ``Module`` to the ``DependencyManager``. | ||
protocol DependencyDeclaration { | ||
/// Request from the ``DependencyManager`` to collect all dependencies. Mark required by calling `DependencyManager/require(_:defaultValue:)`. | ||
func collect(into dependencyManager: DependencyManager) | ||
/// Inject the dependency instance from the ``DependencyManager``. Use `DependencyManager/retrieve(module:)`. | ||
func inject(from dependencyManager: DependencyManager) | ||
} | ||
|
||
extension Module { | ||
var dependencyDeclarations: [DependencyDeclaration] { | ||
retrieveProperties(ofType: DependencyDeclaration.self) | ||
} | ||
} |
This file was deleted.
Oops, something went wrong.
Oops, something went wrong.