1 /*
<lambda>null2 * Copyright (C) 2024 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 package com.android.systemui.kairos.util
18
19 import com.android.systemui.kairos.util.Either.First
20 import com.android.systemui.kairos.util.Either.Second
21 import com.android.systemui.kairos.util.Maybe.Present
22
23 /** A "patch" that can be used to batch-update a [Map], via [applyPatch]. */
24 typealias MapPatch<K, V> = Map<K, Maybe<V>>
25
26 /**
27 * Returns a new [Map] that has [patch] applied to the original map.
28 *
29 * For each entry in [patch]:
30 * * a [Present] value will be included in the new map, replacing the entry in the original map with
31 * the same key, if present.
32 * * a [Maybe.Absent] value will be omitted from the new map, excluding the entry in the original
33 * map with the same key, if present.
34 */
35 fun <K, V> Map<K, V>.applyPatch(patch: MapPatch<K, V>): Map<K, V> {
36 val (adds: List<Pair<K, V>>, removes: List<K>) =
37 patch
38 .asSequence()
39 .map { (k, v) -> if (v is Present) First(k to v.value) else Second(k) }
40 .partitionEithers()
41 val removed: Map<K, V> = this - removes.toSet()
42 val updated: Map<K, V> = removed + adds
43 return updated
44 }
45
46 /**
47 * Returns a [MapPatch] that, when applied, includes all of the values from the original [Map].
48 *
49 * Shorthand for:
50 * ```
51 * mapValues { (key, value) -> Maybe.present(value) }
52 * ```
53 */
<lambda>null54 fun <K, V> Map<K, V>.toMapPatch(): MapPatch<K, V> = mapValues { Maybe.present(it.value) }
55
56 /**
57 * Returns a [MapPatch] that, when applied, includes all of the entries from [new] whose keys are
58 * not present in [old], and excludes all entries with keys present in [old] that are not also
59 * present in [new].
60 *
61 * Note that, unlike [mapPatchFromFullDiff], only keys are taken into account. If the same key is
62 * present in both [old] and [new], but the associated values are not equal, then the returned
63 * [MapPatch] will *not* include any update to that key.
64 */
mapPatchFromKeyDiffnull65 fun <K, V> mapPatchFromKeyDiff(old: Map<K, V>, new: Map<K, V>): MapPatch<K, V> {
66 val removes = old.keys - new.keys
67 val adds = new - old.keys
68 return buildMap {
69 for (removed in removes) {
70 put(removed, Maybe.absent)
71 }
72 for ((newKey, newValue) in adds) {
73 put(newKey, Maybe.present(newValue))
74 }
75 }
76 }
77
78 /**
79 * Returns a [MapPatch] that, when applied, includes all of the entries from [new] that are not
80 * present in [old], and excludes all entries with keys present in [old] that are not also present
81 * in [new].
82 *
83 * Note that, unlike [mapPatchFromKeyDiff], both keys and values are taken into account. If the same
84 * key is present in both [old] and [new], but the associated values are not equal, then the
85 * returned [MapPatch] will include the entry from [new].
86 */
mapPatchFromFullDiffnull87 fun <K, V> mapPatchFromFullDiff(old: Map<K, V>, new: Map<K, V>): MapPatch<K, V> {
88 val removes = old.keys - new.keys
89 val adds =
90 new.mapMaybeValues { (k, v) ->
91 if (k in old && v == old[k]) Maybe.absent else Maybe.present(v)
92 }
93 return hashMapOf<K, Maybe<V>>().apply {
94 for (removed in removes) {
95 put(removed, Maybe.absent)
96 }
97 for ((newKey, newValue) in adds) {
98 put(newKey, Maybe.present(newValue))
99 }
100 }
101 }
102