diff --git a/Sources/FirebladeECS/FSM.swift b/Sources/FirebladeECS/FSM.swift index d9ab4d5..01b22b4 100644 --- a/Sources/FirebladeECS/FSM.swift +++ b/Sources/FirebladeECS/FSM.swift @@ -192,6 +192,66 @@ public class EntityState { } } +/// This extension provides ergonomic way to add component mapping and component +/// provider at once +extension EntityState { + /// Creates a mapping for the component type to a specific component instance. + /// ComponentInstanceProvider is used for the mapping. + /// - Parameter component: The component instance to use for the mapping + /// - Returns: This EntityState, so more modifications can be applied + @discardableResult + @inline(__always) + public func addInstance(_ component: C) -> Self { + add(C.self).withInstance(component) + return self + } + + /// Creates a mapping for the component type to new instances of the provided type. + /// A ComponentTypeProvider is used for the mapping. + /// - Parameter type: The type of components to be created by this mapping + /// - Returns: This EntityState, so more modifications can be applied + @inline(__always) + @discardableResult + public func addType(_ type: ComponentInitializable.Type) -> Self { + add(type).withType(type) + return self + } + + /// Creates a mapping for the component type to a single instance of the provided type. + /// The instance is not created until it is first requested. + /// A ComponentSingletonProvider is used for the mapping. + /// - Parameter type: The type of the single instance to be created. + /// - Returns: This EntityState, so more modifications can be applied + @inline(__always) + @discardableResult + public func addSingleton(_ type: ComponentInitializable.Type) -> Self { + add(type).withSingleton(type) + return self + } + + /// Creates a mapping for the component type to a method call. + /// A DynamicComponentProvider is used for the mapping. + /// - Parameter method: The method to return the component instance + /// - Returns: This EntityState, so more modifications can be applied + @inline(__always) + @discardableResult + public func addMethod(type: C.Type, closure: DynamicComponentProvider.Closure) -> Self { + add(type).withMethod(closure) + return self + } + + /// Creates a mapping for the component type to any ComponentProvider. + /// - Parameter type: The type of component to be mapped + /// - Parameter provider: The component provider to use. + /// - Returns: This EntityState, so more modifications can be applied. + @inline(__always) + @discardableResult + public func addProvider(type: C.Type, provider: ComponentProvider) -> Self { + add(type).withProvider(provider) + return self + } +} + // MARK: - /// Used by the EntityState class to create the mappings of components to providers via a fluent interface. diff --git a/Tests/FirebladeECSTests/FSMTests.swift b/Tests/FirebladeECSTests/FSMTests.swift index 8c466e0..f31c372 100644 --- a/Tests/FirebladeECSTests/FSMTests.swift +++ b/Tests/FirebladeECSTests/FSMTests.swift @@ -270,6 +270,41 @@ class EntityStateTests: XCTestCase { XCTAssertTrue(state.has(MockComponent.self)) } + func testAddInstanceCreatesMappingAndSetsInstanceProviderForInstanceType() { + let component = MockComponent() + state.addInstance(component) + XCTAssertTrue(state.get(MockComponent.self) is ComponentInstanceProvider?) + XCTAssert(state.get(MockComponent.self)?.getComponent() === component) + } + + func testAddTypeCreatesMappingAndSetsTypeProviderForType() { + state.addType(MockComponent.self) + XCTAssertTrue(state.get(MockComponent.self) is ComponentTypeProvider?) + XCTAssertNotNil(state.get(MockComponent.self)?.getComponent()) + XCTAssertTrue(state.get(MockComponent.self)?.getComponent() is MockComponent?) + } + + func testAddSingletonCreatesMappingAndSetsSingletonProviderForType() { + state.addSingleton(MockComponent.self) + XCTAssertTrue(state.get(MockComponent.self) is ComponentSingletonProvider?) + XCTAssertNotNil(state.get(MockComponent.self)?.getComponent()) + XCTAssertTrue(state.get(MockComponent.self)?.getComponent() is MockComponent?) + } + + func testAddMethodCreatesMappingAndSetsDynamicProviderForType() { + let component = MockComponent() + state.addMethod(type: MockComponent.self, closure: .init { component }) + XCTAssertTrue(state.get(MockComponent.self) is DynamicComponentProvider?) + XCTAssertTrue(state.get(MockComponent.self)?.getComponent() === component) + } + + func testAddProviderCreatesMappingAndSetsProvider() { + let provider = ComponentSingletonProvider(type: MockComponent.self) + state.addProvider(type: MockComponent.self, provider: provider) + XCTAssert(state.get(MockComponent.self) is ComponentSingletonProvider?) + XCTAssertNotNil(state.get(MockComponent.self)) + } + class MockComponent: ComponentInitializable { let value: Int diff --git a/Tests/FirebladeECSTests/XCTestManifests.swift b/Tests/FirebladeECSTests/XCTestManifests.swift index 8079fe2..f950812 100644 --- a/Tests/FirebladeECSTests/XCTestManifests.swift +++ b/Tests/FirebladeECSTests/XCTestManifests.swift @@ -113,6 +113,11 @@ extension EntityStateTests { // `swift test --generate-linuxmain` // to regenerate. static let __allTests__EntityStateTests = [ + ("testAddInstanceCreatesMappingAndSetsInstanceProviderForInstanceType", testAddInstanceCreatesMappingAndSetsInstanceProviderForInstanceType), + ("testAddMethodCreatesMappingAndSetsDynamicProviderForType", testAddMethodCreatesMappingAndSetsDynamicProviderForType), + ("testAddProviderCreatesMappingAndSetsProvider", testAddProviderCreatesMappingAndSetsProvider), + ("testAddSingletonCreatesMappingAndSetsSingletonProviderForType", testAddSingletonCreatesMappingAndSetsSingletonProviderForType), + ("testAddTypeCreatesMappingAndSetsTypeProviderForType", testAddTypeCreatesMappingAndSetsTypeProviderForType), ("testAddWithInstanceQualifierCreatesInstanceProvider", testAddWithInstanceQualifierCreatesInstanceProvider), ("testAddWithMethodQualifierCreatesDynamicProvider", testAddWithMethodQualifierCreatesDynamicProvider), ("testAddWithNoQualifierCreatesTypeProvider", testAddWithNoQualifierCreatesTypeProvider),