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