Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Avoid set several times HACachedStates #56

Merged
merged 2 commits into from
Mar 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 17 additions & 16 deletions Source/Caches/HACacheKeyStates.swift
Original file line number Diff line number Diff line change
Expand Up @@ -24,25 +24,26 @@ internal struct HACacheKeyStates: HACacheKey {
/// - shouldResetEntities: True if current state needs to be ignored (e.g. re-connection)
/// - Returns: HAEntity cached states
/// Logic from: https://github.com/home-assistant/home-assistant-js-websocket/blob/master/lib/entities.ts
// swiftlint: disable cyclomatic_complexity
static func processUpdates(
info: HACacheTransformInfo<HACompressedStatesUpdates, HACachedStates?>,
shouldResetEntities: Bool
)
-> HACachedStates {
var states: HACachedStates
if shouldResetEntities {
states = .init(entities: [])
} else {
states = info.current ?? .init(entities: [])
) -> HACachedStates {
var updatedEntities: [String: HAEntity] = [:]

if !shouldResetEntities, let currentEntities = info.current {
updatedEntities = currentEntities.allByEntityId
}

if let additions = info.incoming.add {
for (entityId, updates) in additions {
if var currentState = states[entityId] {
if var currentState = updatedEntities[entityId] {
currentState.update(from: updates)
states[entityId] = currentState
updatedEntities[entityId] = currentState
} else {
do {
states[entityId] = try updates.asEntity(entityId: entityId)
let newEntity = try updates.asEntity(entityId: entityId)
updatedEntities[entityId] = newEntity
} catch {
HAGlobal.log(.error, "[Update-To-Entity-Error] Failed adding new entity: \(error)")
}
Expand All @@ -52,26 +53,26 @@ internal struct HACacheKeyStates: HACacheKey {

if let subtractions = info.incoming.remove {
for entityId in subtractions {
states[entityId] = nil
updatedEntities.removeValue(forKey: entityId)
}
}

if let changes = info.incoming.change {
changes.forEach { entityId, diff in
guard var entityState = states[entityId] else { return }
for (entityId, diff) in changes {
guard var entityState = updatedEntities[entityId] else { continue }

if let toAdd = diff.additions {
entityState.add(toAdd)
states[entityId] = entityState
}

if let toRemove = diff.subtractions {
entityState.subtract(toRemove)
states[entityId] = entityState
}

updatedEntities[entityId] = entityState
}
}

return states
return .init(entitiesDictionary: updatedEntities)
}
}
14 changes: 11 additions & 3 deletions Source/Caches/HACachedStates.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,23 +6,31 @@ public extension HACachesContainer {
/// Cached version of all entity states
public struct HACachedStates {
/// All entities
public var all: Set<HAEntity>
public var all: Set<HAEntity> = []
/// All entities, keyed by their entityId
public subscript(entityID: String) -> HAEntity? {
get { allByEntityId[entityID] }
set { allByEntityId[entityID] = newValue }
}

/// Backing dictionary, whose mutation updates the set
private var allByEntityId: [String: HAEntity] {
internal var allByEntityId: [String: HAEntity] {
didSet {
all = Set(allByEntityId.values)
}
}

/// Create a cached state
/// - Parameter entitiesDictionary: The entities to start with, key is the entity ID
public init(entitiesDictionary: [String: HAEntity]) {
self.all = Set(entitiesDictionary.values)
self.allByEntityId = entitiesDictionary
}

/// Create a cached state
/// Mainly for tests
/// - Parameter entities: The entities to start with
public init(entities: [HAEntity]) {
internal init(entities: [HAEntity]) {
self.all = Set(entities)
self.allByEntityId = entities.reduce(into: [:]) { dictionary, entity in
dictionary[entity.entityId] = entity
Expand Down
2 changes: 1 addition & 1 deletion Source/Data/HAEntity.swift
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ public struct HAEntity: HADataDecodable, Hashable {
attributes: [String: Any],
context: HAResponseEvent.Context
) throws {
var domain: String = try {
let domain: String = try {
if let domain {
domain
} else {
Expand Down
12 changes: 6 additions & 6 deletions Tests/HACacheKeyStates.test.swift
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ internal final class HACacheKeyStates_test: XCTestCase {
)
),
current: .init(
entities: []
entitiesDictionary: [:]
)
), shouldResetEntities: false
)
Expand Down Expand Up @@ -133,8 +133,8 @@ internal final class HACacheKeyStates_test: XCTestCase {
)
),
current: .init(
entities: [
existentEntity,
entitiesDictionary: [
"person.bruno": existentEntity,
]
)
), shouldResetEntities: false
Expand Down Expand Up @@ -202,8 +202,8 @@ internal final class HACacheKeyStates_test: XCTestCase {
)
),
current: .init(
entities: [
existentEntity,
entitiesDictionary: [
"person.bruno": existentEntity,
]
)
), shouldResetEntities: false
Expand Down Expand Up @@ -251,7 +251,7 @@ internal final class HACacheKeyStates_test: XCTestCase {
)
),
current: .init(
entities: []
entitiesDictionary: [:]
)
), shouldResetEntities: false
)
Expand Down
23 changes: 23 additions & 0 deletions Tests/HACachedStates.test.swift
Original file line number Diff line number Diff line change
Expand Up @@ -125,4 +125,27 @@ internal class HACachedStatesTests: XCTestCase {

XCTAssertEqual(entityRemovedOutgoingType?.all.count, 0)
}

func testSubscriptByEntityIdReturnsCorrectly() throws {
let expectedEntity: HAEntity = try .fake(id: "person.bruno")
let cache = HACachedStates(entitiesDictionary: [expectedEntity.entityId: expectedEntity])
XCTAssertEqual(cache["fake.person.bruno"], expectedEntity)
}

func testSubscriptSetByEntityIdSetsCorrectly() throws {
let expectedEntity: HAEntity = try .fake(id: "person.bruno")
let expectedEntity2: HAEntity = try .fake(id: "person.bruno2")
var cache = HACachedStates(entitiesDictionary: [expectedEntity.entityId: expectedEntity])
cache[expectedEntity2.entityId] = expectedEntity2
XCTAssertEqual(cache["fake.person.bruno2"], expectedEntity2)
}

func testSetAllByEntityIdSetsAllToo() throws {
let expectedEntity: HAEntity = try .fake(id: "person.bruno")
let expectedEntity2: HAEntity = try .fake(id: "person.bruno2")
var cache = HACachedStates(entitiesDictionary: [expectedEntity.entityId: expectedEntity])

cache.allByEntityId = [expectedEntity2.entityId: expectedEntity2]
XCTAssertEqual(cache.allByEntityId, [expectedEntity2.entityId: expectedEntity2])
}
}