1 /*
2  * Copyright 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 androidx.camera.camera2.pipe.internal
20 
21 import androidx.camera.camera2.pipe.OutputStatus
22 import kotlinx.coroutines.CompletableDeferred
23 import kotlinx.coroutines.Deferred
24 import kotlinx.coroutines.ExperimentalCoroutinesApi
25 
26 /**
27  * Inline value class that can be used in place of `Any` to represent either a valid output object
28  * OR an [OutputStatus] indicating why the result is not available.
29  */
30 @JvmInline
31 internal value class OutputResult<out T> private constructor(internal val result: Any?) {
32     /** Returns `true` if this instance represents a successful outcome. */
33     val available: Boolean
34         get() = !failure && result != null
35 
36     /** Returns `true` if this instance represents a failed result. */
37     val failure: Boolean
38         get() = result is OutputStatus
39 
40     /** Returns the value, if [from], else null. */
41     @Suppress("UNCHECKED_CAST")
42     inline val output: T?
43         get() =
44             when {
45                 available -> result as T
46                 else -> null
47             }
48 
49     /**
50      * If this OutputResult represents a failure, then return the [OutputStatus] associated with it,
51      * otherwise report [OutputStatus.AVAILABLE] for successfully cases.
52      */
53     inline val status: OutputStatus
54         get() =
55             when {
56                 available -> OutputStatus.AVAILABLE
57                 result == null -> OutputStatus.UNAVAILABLE
58                 else -> result as OutputStatus
59             }
60 
61     @OptIn(ExperimentalCoroutinesApi::class)
62     companion object {
63         /** Returns an instance that encapsulates the given [output] as successful value. */
fromnull64         inline fun <T> from(output: T): OutputResult<T> = OutputResult(output as Any?)
65 
66         /** Returns an instance that encapsulates the given OutputStatus as a failure. */
67         inline fun <T> failure(failureReason: OutputStatus): OutputResult<T> =
68             OutputResult(failureReason)
69 
70         /** Utility function to complete a CompletableDeferred with a successful [OutputResult]. */
71         inline fun <T> CompletableDeferred<OutputResult<T>>.completeWithOutput(output: T): Boolean {
72             return complete(from(output))
73         }
74 
75         /** Utility function to complete a CompletableDeferred with a [OutputStatus] failure */
completeWithFailurenull76         inline fun <T> CompletableDeferred<OutputResult<T>>.completeWithFailure(
77             status: OutputStatus
78         ): Boolean {
79             return complete(failure(status))
80         }
81 
82         /**
83          * For a [Deferred] object that contains an OutputResult, determine the status based on the
84          * state of the [Deferred] or from the actual object if this status has been completed.
85          */
outputStatusnull86         inline fun <T> Deferred<OutputResult<T>>.outputStatus(): OutputStatus {
87             return if (!isCompleted) {
88                 // If the result is not completed, then this Output is in a PENDING state.
89                 OutputStatus.PENDING
90             } else if (isCancelled) {
91                 // If the result was canceled for any reason, then this Output is, and will not, be
92                 // available.
93                 OutputStatus.UNAVAILABLE
94             } else {
95                 // If we reach here, the result is A) completed, and B) not canceled. read the
96                 // status from the result and return it.
97                 getCompleted().status
98             }
99         }
100 
101         /** Get the output from this [Deferred], if available, or null. */
outputOrNullnull102         inline fun <T> Deferred<OutputResult<T>>.outputOrNull(): T? {
103             if (isCompleted && !isCancelled) {
104                 return getCompleted().output
105             }
106             return null
107         }
108     }
109 }
110