1 /*
2 * 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 @file:Suppress("NOTHING_TO_INLINE")
18
19 package com.android.systemui.kairos.util
20
21 import com.android.systemui.kairos.util.Either.First
22 import com.android.systemui.kairos.util.Either.Second
23
24 /**
25 * Contains a value of two possibilities: `First<A>` or `Second<B>`
26 *
27 * [Either] generalizes sealed classes the same way that [Pair] generalizes data classes; if a
28 * [Pair] is effectively an anonymous grouping of two instances, then an [Either] is an anonymous
29 * set of two options.
30 */
31 sealed interface Either<out A, out B> {
32 /** An [Either] that contains a [First] value. */
33 @JvmInline value class First<out A>(val value: A) : Either<A, Nothing>
34
35 /** An [Either] that contains a [Second] value. */
36 @JvmInline value class Second<out B>(val value: B) : Either<Nothing, B>
37
38 companion object {
39 /** Constructs an [Either] containing the first possibility. */
firstnull40 fun <A> first(value: A): Either<A, Nothing> = First(value)
41
42 /** Constructs a [Either] containing the second possibility. */
43 fun <B> second(value: B): Either<Nothing, B> = Second(value)
44 }
45 }
46
47 /**
48 * Returns an [Either] containing the result of applying [transform] to the [First] value, or the
49 * [Second] value unchanged.
50 */
51 inline fun <A, B, C> Either<A, C>.mapFirst(transform: (A) -> B): Either<B, C> =
52 when (this) {
53 is First -> First(transform(value))
54 is Second -> this
55 }
56
57 /**
58 * Returns an [Either] containing the result of applying [transform] to the [Second] value, or the
59 * [First] value unchanged.
60 */
mapSecondnull61 inline fun <A, B, C> Either<A, B>.mapSecond(transform: (B) -> C): Either<A, C> =
62 when (this) {
63 is First -> this
64 is Second -> Second(transform(value))
65 }
66
67 /** Returns a [Maybe] containing the [First] value held by this [Either], if present. */
firstMaybenull68 inline fun <A> Either<A, *>.firstMaybe(): Maybe<A> =
69 when (this) {
70 is First -> Maybe.present(value)
71 else -> Maybe.absent
72 }
73
74 /** Returns the [First] value held by this [Either], or `null` if this is a [Second] value. */
firstOrNullnull75 inline fun <A> Either<A, *>.firstOrNull(): A? =
76 when (this) {
77 is First -> value
78 else -> null
79 }
80
81 /** Returns a [Maybe] containing the [Second] value held by this [Either], if present. */
secondMaybenull82 inline fun <B> Either<*, B>.secondMaybe(): Maybe<B> =
83 when (this) {
84 is Second -> Maybe.present(value)
85 else -> Maybe.absent
86 }
87
88 /** Returns the [Second] value held by this [Either], or `null` if this is a [First] value. */
secondOrNullnull89 inline fun <B> Either<*, B>.secondOrNull(): B? =
90 when (this) {
91 is Second -> value
92 else -> null
93 }
94
95 /**
96 * Returns a [These] containing either the [First] value as [These.first], or the [Second] value as
97 * [These.second]. Will never return a [These.both].
98 */
asThesenull99 fun <A, B> Either<A, B>.asThese(): These<A, B> =
100 when (this) {
101 is Second -> These.second(value)
102 is First -> These.first(value)
103 }
104
105 /**
106 * Partitions this sequence of [Either] into two lists; [Pair.first] contains all [First] values,
107 * and [Pair.second] contains all [Second] values.
108 */
partitionEithersnull109 fun <A, B> Sequence<Either<A, B>>.partitionEithers(): Pair<List<A>, List<B>> {
110 val firsts = mutableListOf<A>()
111 val seconds = mutableListOf<B>()
112 for (either in this) {
113 when (either) {
114 is First -> firsts.add(either.value)
115 is Second -> seconds.add(either.value)
116 }
117 }
118 return firsts to seconds
119 }
120
121 /**
122 * Partitions this map of [Either] values into two maps; [Pair.first] contains all [First] values,
123 * and [Pair.second] contains all [Second] values.
124 */
partitionEithersnull125 fun <K, A, B> Map<K, Either<A, B>>.partitionEithers(): Pair<Map<K, A>, Map<K, B>> {
126 val firsts = mutableMapOf<K, A>()
127 val seconds = mutableMapOf<K, B>()
128 for ((k, e) in this) {
129 when (e) {
130 is First -> firsts[k] = e.value
131 is Second -> seconds[k] = e.value
132 }
133 }
134 return firsts to seconds
135 }
136