xref: /aosp_15_r20/frameworks/base/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/PullNodes.kt (revision d57664e9bc4670b3ecf6748a746a57c557b6bc9e)
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.internal
18 
19 import com.android.systemui.kairos.internal.util.Key
20 import com.android.systemui.kairos.util.Maybe
21 import com.android.systemui.kairos.util.map
22 import kotlinx.coroutines.CoroutineStart
23 import kotlinx.coroutines.Deferred
24 
25 internal val neverImpl: TFlowImpl<Nothing> = TFlowCheap { null }
26 
27 internal class MapNode<A, B>(val upstream: PullNode<A>, val transform: suspend EvalScope.(A) -> B) :
28     PullNode<B> {
getPushEventnull29     override suspend fun getPushEvent(evalScope: EvalScope): Maybe<B> =
30         upstream.getPushEvent(evalScope).map { evalScope.transform(it) }
31 }
32 
mapImplnull33 internal inline fun <A, B> mapImpl(
34     crossinline upstream: suspend EvalScope.() -> TFlowImpl<A>,
35     noinline transform: suspend EvalScope.(A) -> B,
36 ): TFlowImpl<B> = TFlowCheap { downstream ->
37     upstream().activate(evalScope = this, downstream)?.let { (connection, needsEval) ->
38         ActivationResult(
39             connection =
40                 NodeConnection(
41                     directUpstream = MapNode(connection.directUpstream, transform),
42                     schedulerUpstream = connection.schedulerUpstream,
43                 ),
44             needsEval = needsEval,
45         )
46     }
47 }
48 
49 internal class CachedNode<A>(val key: Key<Deferred<Maybe<A>>>, val upstream: PullNode<A>) :
50     PullNode<A> {
getPushEventnull51     override suspend fun getPushEvent(evalScope: EvalScope): Maybe<A> {
52         val deferred =
53             evalScope.transactionStore.getOrPut(key) {
54                 evalScope.deferAsync(CoroutineStart.LAZY) { upstream.getPushEvent(evalScope) }
55             }
56         return deferred.await()
57     }
58 }
59 
cachednull60 internal fun <A> TFlowImpl<A>.cached(): TFlowImpl<A> {
61     val key = object : Key<Deferred<Maybe<A>>> {}
62     return TFlowCheap {
63         activate(this, it)?.let { (connection, needsEval) ->
64             ActivationResult(
65                 connection =
66                     NodeConnection(
67                         directUpstream = CachedNode(key, connection.directUpstream),
68                         schedulerUpstream = connection.schedulerUpstream,
69                     ),
70                 needsEval = needsEval,
71             )
72         }
73     }
74 }
75