InspectableValue.kt

/*
 * Copyright 2020 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package androidx.compose.ui.platform

import androidx.compose.runtime.InternalComposeApi

/**
 * An empty [InspectorInfo] DSL.
 */
val NoInspectorInfo: InspectorInfo.() -> Unit = {}

/**
 * Turn on inspector debug information. Used internally during inspection.
 */
var isDebugInspectorInfoEnabled = false

/**
 * A compose value that is inspectable by tools. It gives access to private parts of a value.
 */
interface InspectableValue {

    /**
     * The elements of a compose value.
     */
    val inspectableElements: Sequence<ValueElement>
        get() = emptySequence()

    /**
     * Use this name as the reference name shown in tools of this value if there is no explicit
     * reference name given to the value.
     * Example: a modifier in a modifier list.
     */
    val nameFallback: String?
        get() = null

    /**
     * Use this value as a readable representation of the value.
     */
    val valueOverride: Any?
        get() = null
}

/**
 * A [ValueElement] describes an element of a compose value instance.
 * The [name] typically refers to a (possibly private) property name with its corresponding [value].
 */
data class ValueElement(val name: String, val value: Any?)

/**
 * A builder for an [InspectableValue].
 */
class InspectorInfo {
    /**
     * Provides a [InspectableValue.nameFallback].
     */
    var name: String? = null

    /**
     * Provides a [InspectableValue.valueOverride].
     */
    var value: Any? = null

    /**
     * Provides a [InspectableValue.inspectableElements].
     */
    val properties = ValueElementSequence()
}

/**
 * A builder for a sequence of [ValueElement].
 */
class ValueElementSequence : Sequence<ValueElement> {
    private val elements = mutableListOf<ValueElement>()

    override fun iterator(): Iterator<ValueElement> = elements.iterator()

    /**
     * Specify a sub element with name and value.
     */
    operator fun set(name: String, value: Any?) {
        elements.add(ValueElement(name, value))
    }
}

/**
 * Implementation of [InspectableValue] based on a builder [InspectorInfo] DSL.
 */
abstract class InspectorValueInfo(private val info: InspectorInfo.() -> Unit) : InspectableValue {
    private var _values: InspectorInfo? = null

    private val values: InspectorInfo
        get() {
            val valueInfo = _values ?: InspectorInfo().apply { info() }
            _values = valueInfo
            return valueInfo
        }

    override val nameFallback: String?
        get() = values.name

    override val valueOverride: Any?
        get() = values.value

    override val inspectableElements: Sequence<ValueElement>
        get() = values.properties
}

/**
 * Factory method for avoiding DSL allocation when no debug inspector info is needed.
 */
inline fun debugInspectorInfo(
    crossinline definitions: InspectorInfo.() -> Unit
): InspectorInfo.() -> Unit =
    @OptIn(InternalComposeApi::class)
    if (isDebugInspectorInfoEnabled) ({ definitions() }) else NoInspectorInfo