Add initial implementation of component providers for entity FSM
This commit is contained in:
parent
36e8c79e82
commit
acc2a0feff
|
|
@ -0,0 +1,167 @@
|
|||
public protocol EmptyInitializable {
|
||||
init()
|
||||
}
|
||||
|
||||
public typealias ComponentInitializable = Component & EmptyInitializable
|
||||
|
||||
public protocol ComponentProvider {
|
||||
var identifier: AnyHashable { get }
|
||||
func getComponent<C>() -> C? where C: Component
|
||||
}
|
||||
|
||||
// MARK: -
|
||||
|
||||
public struct ComponentInstanceProvider {
|
||||
private var instance: Component
|
||||
|
||||
public init(instance: Component) {
|
||||
self.instance = instance
|
||||
}
|
||||
}
|
||||
|
||||
extension ComponentInstanceProvider: ComponentProvider {
|
||||
public var identifier: AnyHashable {
|
||||
ObjectIdentifier(instance)
|
||||
}
|
||||
|
||||
public func getComponent<C>() -> C? where C : Component {
|
||||
instance as? C
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: -
|
||||
|
||||
public struct ComponentTypeProvider {
|
||||
private var componentType: ComponentInitializable.Type
|
||||
public let identifier: AnyHashable
|
||||
|
||||
public init<T: ComponentInitializable>(type: T.Type) {
|
||||
componentType = type
|
||||
identifier = ObjectIdentifier(componentType.self)
|
||||
}
|
||||
}
|
||||
|
||||
extension ComponentTypeProvider: ComponentProvider {
|
||||
public func getComponent<C>() -> C? where C: Component {
|
||||
componentType.init() as? C
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: -
|
||||
|
||||
public class ComponentSingletonProvider {
|
||||
lazy private var instance: Component = {
|
||||
componentType.init()
|
||||
}()
|
||||
private var componentType: ComponentInitializable.Type
|
||||
|
||||
public var identifier: AnyHashable {
|
||||
ObjectIdentifier(instance)
|
||||
}
|
||||
|
||||
public init<T: ComponentInitializable>(type: T.Type) {
|
||||
componentType = type
|
||||
}
|
||||
|
||||
internal init(type: ComponentInitializable.Type) {
|
||||
componentType = type
|
||||
}
|
||||
}
|
||||
|
||||
extension ComponentSingletonProvider: ComponentProvider {
|
||||
public func getComponent<C>() -> C? where C: Component {
|
||||
instance as? C
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: -
|
||||
|
||||
public struct DynamicComponentProvider {
|
||||
public class Closure {
|
||||
let closure: () -> Component
|
||||
public init(closure: @escaping () -> Component) {
|
||||
self.closure = closure
|
||||
}
|
||||
}
|
||||
private let closure: Closure
|
||||
|
||||
public init(closure: Closure) {
|
||||
self.closure = closure
|
||||
}
|
||||
}
|
||||
|
||||
extension DynamicComponentProvider: ComponentProvider {
|
||||
public var identifier: AnyHashable {
|
||||
ObjectIdentifier(closure)
|
||||
}
|
||||
|
||||
public func getComponent<C>() -> C? where C: Component {
|
||||
closure.closure() as? C
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: -
|
||||
|
||||
public class EntityState {
|
||||
internal var providers = [ComponentIdentifier: ComponentProvider]()
|
||||
|
||||
public init() { }
|
||||
|
||||
public func add<C: ComponentInitializable>(_ type: C.Type) -> StateComponentMapping {
|
||||
StateComponentMapping(creatingState: self,
|
||||
type: type)
|
||||
}
|
||||
}
|
||||
// MARK: -
|
||||
|
||||
public class StateComponentMapping {
|
||||
private var componentType: ComponentInitializable.Type
|
||||
private let creatingState: EntityState
|
||||
private var provider: ComponentProvider
|
||||
|
||||
public init<T: ComponentInitializable>(creatingState: EntityState, type: T.Type) {
|
||||
self.creatingState = creatingState
|
||||
componentType = type
|
||||
provider = ComponentTypeProvider(type: type)
|
||||
}
|
||||
|
||||
public func withInstance(_ component: Component) -> StateComponentMapping {
|
||||
setProvider(ComponentInstanceProvider(instance: component))
|
||||
return self
|
||||
}
|
||||
|
||||
public func withType<T: ComponentInitializable>(_ type: T.Type) -> Self {
|
||||
setProvider(ComponentTypeProvider(type: type))
|
||||
return self
|
||||
}
|
||||
|
||||
public func withSingleton<T: ComponentInitializable>(_ type: T.Type?) -> Self {
|
||||
if let type = type {
|
||||
setProvider(ComponentSingletonProvider(type: type))
|
||||
} else {
|
||||
setProvider(ComponentSingletonProvider(type: componentType))
|
||||
}
|
||||
|
||||
return self
|
||||
}
|
||||
|
||||
public func withMethod(_ closure: DynamicComponentProvider.Closure) -> Self {
|
||||
setProvider(DynamicComponentProvider(closure: closure))
|
||||
return self
|
||||
}
|
||||
|
||||
public func withProvider(_ provider: ComponentProvider) -> Self {
|
||||
setProvider(provider)
|
||||
return self
|
||||
}
|
||||
|
||||
public func add<T: ComponentInitializable>(_ type: T.Type) -> StateComponentMapping {
|
||||
creatingState.add(type)
|
||||
}
|
||||
|
||||
private func setProvider(_ provider: ComponentProvider) {
|
||||
self.provider = provider
|
||||
creatingState.providers[componentType.identifier] = provider
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,155 @@
|
|||
import FirebladeECS
|
||||
import XCTest
|
||||
|
||||
class ComponentInstanceProviderTests: XCTestCase {
|
||||
func testProviderReturnsTheInstance() {
|
||||
let instance = MockComponent(value: .max)
|
||||
let provider1 = ComponentInstanceProvider(instance: instance)
|
||||
let providedComponent: MockComponent? = provider1.getComponent()
|
||||
XCTAssertTrue(providedComponent === instance)
|
||||
}
|
||||
|
||||
func testProvidersWithSameInstanceHaveSameIdentifier() {
|
||||
let instance = MockComponent(value: .max)
|
||||
let provider1 = ComponentInstanceProvider(instance: instance)
|
||||
let provider2 = ComponentInstanceProvider(instance: instance)
|
||||
XCTAssertEqual(provider1.identifier, provider2.identifier)
|
||||
}
|
||||
|
||||
func testProvidersWithDifferentInstanceHaveDifferentIdentifier() {
|
||||
let provider1 = ComponentInstanceProvider(instance: MockComponent(value: .max))
|
||||
let provider2 = ComponentInstanceProvider(instance: MockComponent(value: .max))
|
||||
XCTAssertNotEqual(provider1.identifier, provider2.identifier)
|
||||
}
|
||||
|
||||
class MockComponent: Component {
|
||||
var value: Int
|
||||
|
||||
init(value: Int) {
|
||||
self.value = value
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class ComponentTypeProviderTests: XCTestCase {
|
||||
func testProviderReturnsAnInstanceOfType() {
|
||||
let provider = ComponentTypeProvider(type: MockComponent.self)
|
||||
let component: MockComponent? = provider.getComponent()
|
||||
XCTAssertNotNil(component)
|
||||
}
|
||||
|
||||
func testProviderReturnsNewInstanceEachTime() {
|
||||
let provider = ComponentTypeProvider(type: MockComponent.self)
|
||||
let component1: MockComponent? = provider.getComponent()
|
||||
let component2: MockComponent? = provider.getComponent()
|
||||
XCTAssertFalse(component1 === component2)
|
||||
}
|
||||
|
||||
func testProvidersWithSameTypeHaveSameIdentifier() {
|
||||
let provider1 = ComponentTypeProvider(type: MockComponent.self)
|
||||
let provider2 = ComponentTypeProvider(type: MockComponent.self)
|
||||
XCTAssertEqual(provider1.identifier, provider2.identifier)
|
||||
}
|
||||
|
||||
func testProvidersWithDifferentTypeHaveDifferentIdentifier() {
|
||||
let provider1 = ComponentTypeProvider(type: MockComponent.self)
|
||||
let provider2 = ComponentTypeProvider(type: MockComponent2.self)
|
||||
XCTAssertNotEqual(provider1.identifier, provider2.identifier)
|
||||
}
|
||||
|
||||
class MockComponent: Component, EmptyInitializable {
|
||||
var value: String
|
||||
|
||||
required init() {
|
||||
self.value = ""
|
||||
}
|
||||
}
|
||||
|
||||
class MockComponent2: Component, EmptyInitializable {
|
||||
var value: Bool
|
||||
|
||||
required init() {
|
||||
self.value = false
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class ComponentSingletonProviderTests: XCTestCase {
|
||||
func testProviderReturnsAnInstanceOfType() {
|
||||
let provider = ComponentSingletonProvider(type: MockComponent.self)
|
||||
let component: MockComponent? = provider.getComponent()
|
||||
XCTAssertNotNil(component)
|
||||
}
|
||||
|
||||
func testProviderReturnsSameInstanceEachTime() {
|
||||
let provider = ComponentSingletonProvider(type: MockComponent.self)
|
||||
let component1: MockComponent? = provider.getComponent()
|
||||
let component2: MockComponent? = provider.getComponent()
|
||||
XCTAssertTrue(component1 === component2)
|
||||
|
||||
}
|
||||
|
||||
func testProvidersWithSameTypeHaveDifferentIdentifier() {
|
||||
let provider1 = ComponentSingletonProvider(type: MockComponent.self)
|
||||
let provider2 = ComponentSingletonProvider(type: MockComponent.self)
|
||||
XCTAssertNotEqual(provider1.identifier, provider2.identifier)
|
||||
}
|
||||
|
||||
func testProvidersWithDifferentTypeHaveDifferentIdentifier() {
|
||||
let provider1 = ComponentSingletonProvider(type: MockComponent.self)
|
||||
let provider2 = ComponentSingletonProvider(type: MockComponent2.self)
|
||||
XCTAssertNotEqual(provider1.identifier, provider2.identifier)
|
||||
}
|
||||
|
||||
class MockComponent: Component, EmptyInitializable {
|
||||
var value: Int
|
||||
|
||||
required init() {
|
||||
self.value = 0
|
||||
}
|
||||
}
|
||||
|
||||
class MockComponent2: Component, EmptyInitializable {
|
||||
var value: String
|
||||
|
||||
required init() {
|
||||
self.value = ""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class DynamicComponentProviderTests: XCTestCase {
|
||||
func testProviderReturnsTheInstance() {
|
||||
let instance = MockComponent(value: 0)
|
||||
let providerMethod = DynamicComponentProvider.Closure { instance }
|
||||
let provider = DynamicComponentProvider(closure: providerMethod)
|
||||
let component: MockComponent? = provider.getComponent()
|
||||
XCTAssertTrue(component === instance)
|
||||
}
|
||||
|
||||
func testProvidersWithSameMethodHaveSameIdentifier() {
|
||||
let instance = MockComponent(value: 0)
|
||||
let providerMethod = DynamicComponentProvider.Closure { instance }
|
||||
let provider1 = DynamicComponentProvider(closure: providerMethod)
|
||||
let provider2 = DynamicComponentProvider(closure: providerMethod)
|
||||
XCTAssertEqual(provider1.identifier, provider2.identifier)
|
||||
}
|
||||
|
||||
func testProvidersWithDifferentMethodsHaveDifferentIdentifier() {
|
||||
let instance = MockComponent(value: 0)
|
||||
let providerMethod1 = DynamicComponentProvider.Closure { instance }
|
||||
let providerMethod2 = DynamicComponentProvider.Closure { instance }
|
||||
let provider1 = DynamicComponentProvider(closure: providerMethod1)
|
||||
let provider2 = DynamicComponentProvider(closure: providerMethod2)
|
||||
XCTAssertNotEqual(provider1.identifier, provider2.identifier)
|
||||
}
|
||||
|
||||
class MockComponent: Component {
|
||||
let value: Int
|
||||
|
||||
init(value: Int) {
|
||||
self.value = value
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue