All Downloads are FREE. Search and download functionalities are using the official Maven repository.

commonMain.com.dokar.quickjs.binding.DslObjectBinding.kt Maven / Gradle / Ivy

The newest version!
package com.dokar.quickjs.binding

import com.dokar.quickjs.QuickJs
import com.dokar.quickjs.converter.TypeConverters
import com.dokar.quickjs.converter.canConvertReturnInternally
import com.dokar.quickjs.converter.typeOfInstance
import com.dokar.quickjs.qjsError
import kotlin.reflect.typeOf

internal class DslObjectBinding(
    private val scope: ObjectBindingScopeImpl,
    private val quickJs: QuickJs,
) : ObjectBinding {
    private val propertiesDef = scope.properties.associateBy { it.name }
    private val functionsDef = scope.functions.associateBy { it.name }

    override val properties: List = scope.properties.toJsProperties()

    override val functions: List = scope.functions.toJsFunctions()

    override fun getter(name: String): Any? {
        val prop = propertiesDef[name]
            ?: qjsError("Property '$name' not found on object '${scope.name}'")
        val propGetter = prop.getter ?: qjsError("The getter of property '$name' is null")
        return propGetter()
    }

    override fun setter(name: String, value: Any?) {
        val prop = propertiesDef[name]
            ?: qjsError("Property '$name' not found on object '${scope.name}'")
        val propSetter = prop.setter ?: qjsError("The setter of property '$name' is null")
        propSetter(value)
    }

    override fun invoke(name: String, args: Array): Any? {
        val func = functionsDef[name]
            ?: qjsError("Function '$name' not found on object '${scope.name}'")
        val result = when (val call = func.call) {
            is AsyncFunctionBinding<*> -> quickJs.invokeAsyncFunction(args) { call.invoke(it) }
            is FunctionBinding<*> -> call.invoke(args)
            is ObjectBinding -> qjsError("Object cannot be invoked!")
        }

        if (canConvertReturnInternally(result)) {
            return result
        }

        // Convert result to JsObject
        val typeConverters = quickJs.typeConverters
        return typeConverters.convert(
            source = result,
            sourceType = typeOfInstance(typeConverters, result),
            targetType = typeOf(),
        )
    }
}

private fun List>.toJsProperties(): List = map {
    JsProperty(
        name = it.name,
        configurable = it.configurable,
        writable = it.writable ?: (it.setter != null),
        enumerable = it.enumerable,
    )
}

private fun List.toJsFunctions(): List = map {
    JsFunction(
        name = it.name,
        isAsync = it.call is AsyncFunctionBinding<*>,
    )
}

@PublishedApi
internal class ObjectBindingScopeImpl(
    val typeConverters: TypeConverters,
    val name: String,
) : ObjectBindingScope {
    val properties = mutableListOf>()

    val functions = mutableListOf()

    val subScopes = mutableListOf()

    override fun define(name: String, block: ObjectBindingScope.() -> Unit) {
        subScopes.add(
            ObjectBindingScopeImpl(
                typeConverters = typeConverters,
                name = name,
            ).also(block)
        )
    }

    override fun  property(name: String, block: PropertyScope.() -> Unit) {
        val prop = DslProperty(name = name).also(block)
        if (prop.getter == null) {
            qjsError("property($name) requires a getter {}.")
        }
        properties.add(prop)
    }

    override fun  function(name: String, block: FunctionBinding) {
        functions.add(DslFunction(name = name, call = block))
    }

    override fun  asyncFunction(name: String, block: AsyncFunctionBinding) {
        functions.add(DslFunction(name = name, call = block))
    }
}

internal class DslProperty(
    val name: String,
    override var configurable: Boolean = true,
    override var writable: Boolean? = null,
    override var enumerable: Boolean = true,
) : PropertyScope {
    var getter: (() -> Any?)? = null
    var setter: ((Any?) -> Unit)? = null

    override fun getter(block: () -> T) {
        this.getter = { block() as Any }
    }

    @Suppress("unchecked_cast")
    override fun setter(block: (value: T) -> Unit) {
        this.setter = { block(it as T) }
    }
}

internal class DslFunction(
    val name: String,
    val call: Binding,
)




© 2015 - 2024 Weber Informatics LLC | Privacy Policy