mirror of
https://github.com/divkit/divkit.git
synced 2026-06-06 20:07:59 +00:00
Added DivContextComponent
commit_hash:abb1eac222c9e3e86cc2d1eb2fdac23edb091fab
This commit is contained in:
@@ -600,8 +600,14 @@
|
||||
"client/android/compose/proguard-rules.pro":"divkit/public/client/android/compose/proguard-rules.pro",
|
||||
"client/android/compose/src/main/kotlin/com/yandex/div/compose/DivComposeConfiguration.kt":"divkit/public/client/android/compose/src/main/kotlin/com/yandex/div/compose/DivComposeConfiguration.kt",
|
||||
"client/android/compose/src/main/kotlin/com/yandex/div/compose/DivContext.kt":"divkit/public/client/android/compose/src/main/kotlin/com/yandex/div/compose/DivContext.kt",
|
||||
"client/android/compose/src/main/kotlin/com/yandex/div/compose/DivReporter.kt":"divkit/public/client/android/compose/src/main/kotlin/com/yandex/div/compose/DivReporter.kt",
|
||||
"client/android/compose/src/main/kotlin/com/yandex/div/compose/DivView.kt":"divkit/public/client/android/compose/src/main/kotlin/com/yandex/div/compose/DivView.kt",
|
||||
"client/android/compose/src/main/kotlin/com/yandex/div/compose/DivViewPreview.kt":"divkit/public/client/android/compose/src/main/kotlin/com/yandex/div/compose/DivViewPreview.kt",
|
||||
"client/android/compose/src/main/kotlin/com/yandex/div/compose/dagger/DivContextComponent.kt":"divkit/public/client/android/compose/src/main/kotlin/com/yandex/div/compose/dagger/DivContextComponent.kt",
|
||||
"client/android/compose/src/main/kotlin/com/yandex/div/compose/dagger/DivContextModule.kt":"divkit/public/client/android/compose/src/main/kotlin/com/yandex/div/compose/dagger/DivContextModule.kt",
|
||||
"client/android/compose/src/main/kotlin/com/yandex/div/compose/dagger/DivContextScope.kt":"divkit/public/client/android/compose/src/main/kotlin/com/yandex/div/compose/dagger/DivContextScope.kt",
|
||||
"client/android/compose/src/main/kotlin/com/yandex/div/compose/expressions/DivComposeExpressionResolver.kt":"divkit/public/client/android/compose/src/main/kotlin/com/yandex/div/compose/expressions/DivComposeExpressionResolver.kt",
|
||||
"client/android/compose/src/main/kotlin/com/yandex/div/compose/expressions/EvaluatorWarningSender.kt":"divkit/public/client/android/compose/src/main/kotlin/com/yandex/div/compose/expressions/EvaluatorWarningSender.kt",
|
||||
"client/android/compose/src/main/kotlin/com/yandex/div/compose/views/DivBlockView.kt":"divkit/public/client/android/compose/src/main/kotlin/com/yandex/div/compose/views/DivBlockView.kt",
|
||||
"client/android/compose/src/main/kotlin/com/yandex/div/compose/views/DivContainerView.kt":"divkit/public/client/android/compose/src/main/kotlin/com/yandex/div/compose/views/DivContainerView.kt",
|
||||
"client/android/compose/src/main/kotlin/com/yandex/div/compose/views/DivImageView.kt":"divkit/public/client/android/compose/src/main/kotlin/com/yandex/div/compose/views/DivImageView.kt",
|
||||
@@ -610,6 +616,7 @@
|
||||
"client/android/compose/src/main/kotlin/com/yandex/div/compose/views/modifiers/BackgroundModifiers.kt":"divkit/public/client/android/compose/src/main/kotlin/com/yandex/div/compose/views/modifiers/BackgroundModifiers.kt",
|
||||
"client/android/compose/src/main/kotlin/com/yandex/div/compose/views/modifiers/Modifiers.kt":"divkit/public/client/android/compose/src/main/kotlin/com/yandex/div/compose/views/modifiers/Modifiers.kt",
|
||||
"client/android/compose/src/main/kotlin/com/yandex/div/compose/views/modifiers/SizeModifiers.kt":"divkit/public/client/android/compose/src/main/kotlin/com/yandex/div/compose/views/modifiers/SizeModifiers.kt",
|
||||
"client/android/compose/src/test/kotlin/com/yandex/div/compose/expressions/DivComposeExpressionResolverTest.kt":"divkit/public/client/android/compose/src/test/kotlin/com/yandex/div/compose/expressions/DivComposeExpressionResolverTest.kt",
|
||||
"client/android/div-common.gradle":"divkit/public/client/android/div-common.gradle",
|
||||
"client/android/div-core/build.gradle":"divkit/public/client/android/div-core/build.gradle",
|
||||
"client/android/div-core/proguard-rules.pro":"divkit/public/client/android/div-core/proguard-rules.pro",
|
||||
|
||||
@@ -1,16 +1,21 @@
|
||||
plugins {
|
||||
alias(libs.plugins.android.library)
|
||||
alias(libs.plugins.kotlin.compose)
|
||||
alias(libs.plugins.kotlin.ksp)
|
||||
}
|
||||
|
||||
apply(from = "../div-library.gradle")
|
||||
apply(from = "../div-tests.gradle")
|
||||
|
||||
android {
|
||||
namespace = "com.yandex.div.compose"
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(project(":div-core"))
|
||||
implementation(project(":div-data"))
|
||||
implementation(project(":div-evaluable"))
|
||||
implementation(project(":logging"))
|
||||
|
||||
implementation(platform(libs.androidx.compose.bom))
|
||||
implementation(libs.androidx.compose.material3)
|
||||
@@ -18,6 +23,9 @@ dependencies {
|
||||
implementation(libs.androidx.coreKtx)
|
||||
implementation(libs.coil.compose)
|
||||
implementation(libs.coil.network)
|
||||
implementation(libs.yatagan.api.compiled)
|
||||
|
||||
ksp(libs.yatagan.processor.ksp)
|
||||
|
||||
debugImplementation(libs.androidx.compose.ui.tooling)
|
||||
}
|
||||
|
||||
+16
-8
@@ -1,13 +1,21 @@
|
||||
package com.yandex.div.compose
|
||||
|
||||
import android.content.Context
|
||||
import com.yandex.div.json.expressions.ExpressionResolver
|
||||
import com.yandex.div.compose.dagger.`Yatagan$DivContextComponent`
|
||||
import com.yandex.div.core.annotations.PublicApi
|
||||
import com.yandex.yatagan.Module
|
||||
import com.yandex.yatagan.Provides
|
||||
|
||||
class DivComposeConfiguration {
|
||||
fun createContext(baseContext: Context): DivContext {
|
||||
return DivContext(
|
||||
baseContext = baseContext,
|
||||
expressionResolver = ExpressionResolver.EMPTY
|
||||
)
|
||||
}
|
||||
@Module
|
||||
@PublicApi
|
||||
class DivComposeConfiguration(
|
||||
@get:Provides val reporter: DivReporter = DivReporter()
|
||||
)
|
||||
|
||||
fun DivComposeConfiguration.createContext(baseContext: Context): DivContext {
|
||||
return `Yatagan$DivContextComponent`.builder()
|
||||
.baseContext(baseContext)
|
||||
.configuration(this)
|
||||
.build()
|
||||
.context
|
||||
}
|
||||
|
||||
@@ -3,9 +3,13 @@ package com.yandex.div.compose
|
||||
import android.content.Context
|
||||
import android.content.ContextWrapper
|
||||
import androidx.annotation.MainThread
|
||||
import com.yandex.div.core.annotations.PublicApi
|
||||
import com.yandex.div.json.expressions.ExpressionResolver
|
||||
import javax.inject.Inject
|
||||
|
||||
class DivContext @MainThread internal constructor(
|
||||
@PublicApi
|
||||
class DivContext @Inject @MainThread internal constructor(
|
||||
baseContext: Context,
|
||||
internal val expressionResolver: ExpressionResolver
|
||||
internal val expressionResolver: ExpressionResolver,
|
||||
internal val reporter: DivReporter
|
||||
) : ContextWrapper(baseContext)
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
package com.yandex.div.compose
|
||||
|
||||
import com.yandex.div.core.annotations.PublicApi
|
||||
import com.yandex.div.internal.Log
|
||||
|
||||
@PublicApi
|
||||
open class DivReporter {
|
||||
|
||||
open fun reportError(message: String) {
|
||||
Log.e(TAG, message)
|
||||
}
|
||||
|
||||
open fun reportError(message: String, e: Throwable) {
|
||||
Log.e(TAG, message, e)
|
||||
}
|
||||
|
||||
open fun reportWarning(message: String) {
|
||||
Log.w(TAG, message)
|
||||
}
|
||||
}
|
||||
|
||||
private const val TAG = "DivReporter"
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
package com.yandex.div.compose.dagger
|
||||
|
||||
import android.content.Context
|
||||
import com.yandex.div.compose.DivComposeConfiguration
|
||||
import com.yandex.div.compose.DivContext
|
||||
import com.yandex.yatagan.BindsInstance
|
||||
import com.yandex.yatagan.Component
|
||||
|
||||
@DivContextScope
|
||||
@Component(
|
||||
modules = [
|
||||
DivComposeConfiguration::class,
|
||||
DivContextModule::class
|
||||
]
|
||||
)
|
||||
internal interface DivContextComponent {
|
||||
|
||||
val context: DivContext
|
||||
|
||||
@Component.Builder
|
||||
interface Builder {
|
||||
|
||||
@BindsInstance
|
||||
fun baseContext(baseContext: Context): Builder
|
||||
|
||||
@BindsInstance
|
||||
fun configuration(configuration: DivComposeConfiguration): Builder
|
||||
|
||||
fun build(): DivContextComponent
|
||||
}
|
||||
}
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
package com.yandex.div.compose.dagger
|
||||
|
||||
import com.yandex.div.compose.expressions.DivComposeExpressionResolver
|
||||
import com.yandex.div.compose.expressions.EvaluatorWarningSender
|
||||
import com.yandex.div.evaluable.EvaluationContext
|
||||
import com.yandex.div.evaluable.Evaluator
|
||||
import com.yandex.div.evaluable.function.GeneratedBuiltinFunctionProvider
|
||||
import com.yandex.div.json.expressions.ExpressionResolver
|
||||
import com.yandex.yatagan.Binds
|
||||
import com.yandex.yatagan.Module
|
||||
import com.yandex.yatagan.Provides
|
||||
|
||||
@Module
|
||||
internal interface DivContextModule {
|
||||
|
||||
@Binds
|
||||
fun bindExpressionResolver(impl: DivComposeExpressionResolver): ExpressionResolver
|
||||
|
||||
companion object {
|
||||
|
||||
@DivContextScope
|
||||
@Provides
|
||||
fun providerEvaluator(
|
||||
warningSender: EvaluatorWarningSender
|
||||
): Evaluator {
|
||||
val evaluationContext = EvaluationContext(
|
||||
variableProvider = { _ -> null },
|
||||
storedValueProvider = { _ -> null },
|
||||
functionProvider = GeneratedBuiltinFunctionProvider,
|
||||
warningSender = warningSender
|
||||
)
|
||||
return Evaluator(evaluationContext)
|
||||
}
|
||||
}
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
package com.yandex.div.compose.dagger
|
||||
|
||||
import javax.inject.Scope
|
||||
|
||||
@Scope
|
||||
@Retention(AnnotationRetention.RUNTIME)
|
||||
internal annotation class DivContextScope
|
||||
+187
@@ -0,0 +1,187 @@
|
||||
package com.yandex.div.compose.expressions
|
||||
|
||||
import com.yandex.div.compose.DivReporter
|
||||
import com.yandex.div.compose.dagger.DivContextScope
|
||||
import com.yandex.div.core.Disposable
|
||||
import com.yandex.div.core.ObserverList
|
||||
import com.yandex.div.evaluable.Evaluable
|
||||
import com.yandex.div.evaluable.EvaluableException
|
||||
import com.yandex.div.evaluable.Evaluator
|
||||
import com.yandex.div.evaluable.MissingVariableException
|
||||
import com.yandex.div.internal.parser.Converter
|
||||
import com.yandex.div.internal.parser.TypeHelper
|
||||
import com.yandex.div.internal.parser.ValueValidator
|
||||
import com.yandex.div.json.ParsingErrorLogger
|
||||
import com.yandex.div.json.expressions.ExpressionResolver
|
||||
import com.yandex.div.json.invalidValue
|
||||
import com.yandex.div.json.missingVariable
|
||||
import com.yandex.div.json.resolveFailed
|
||||
import com.yandex.div.json.typeMismatch
|
||||
import javax.inject.Inject
|
||||
|
||||
@DivContextScope
|
||||
internal class DivComposeExpressionResolver @Inject constructor(
|
||||
private val evaluator: Evaluator,
|
||||
private val reporter: DivReporter
|
||||
) : ExpressionResolver {
|
||||
|
||||
private val evaluationsCache = mutableMapOf<String, Any>()
|
||||
private val varToExpressions = mutableMapOf<String, MutableSet<String>>()
|
||||
private val expressionObservers = mutableMapOf<String, ObserverList<() -> Unit>>()
|
||||
|
||||
override fun <R, T : Any> get(
|
||||
expressionKey: String,
|
||||
rawExpression: String,
|
||||
evaluable: Evaluable,
|
||||
converter: Converter<R, T?>?,
|
||||
validator: ValueValidator<T>,
|
||||
fieldType: TypeHelper<T>,
|
||||
logger: ParsingErrorLogger
|
||||
): T {
|
||||
return try {
|
||||
tryResolve(expressionKey, rawExpression, evaluable, converter, validator, fieldType)
|
||||
} catch (e: Exception) {
|
||||
reporter.reportError("Failed to resolve expression", e)
|
||||
throw e
|
||||
}
|
||||
}
|
||||
|
||||
override fun subscribeToExpression(
|
||||
rawExpression: String,
|
||||
variableNames: List<String>,
|
||||
callback: () -> Unit
|
||||
): Disposable {
|
||||
variableNames.forEach {
|
||||
val expressions = varToExpressions.getOrPut(it) { mutableSetOf() }
|
||||
expressions.add(rawExpression)
|
||||
}
|
||||
val observers = expressionObservers.getOrPut(rawExpression) { ObserverList() }
|
||||
observers.addObserver(callback)
|
||||
return Disposable { expressionObservers[rawExpression]?.removeObserver(callback) }
|
||||
}
|
||||
|
||||
private fun <R : Any, T : Any> tryResolve(
|
||||
expressionKey: String,
|
||||
rawExpression: String,
|
||||
evaluable: Evaluable,
|
||||
converter: Converter<R, T?>?,
|
||||
validator: ValueValidator<T>,
|
||||
fieldType: TypeHelper<T>,
|
||||
): T {
|
||||
val result = try {
|
||||
getEvaluationResult<R>(rawExpression, evaluable)
|
||||
} catch (e: EvaluableException) {
|
||||
val variableName = tryGetMissingVariableName(e)
|
||||
if (variableName != null) {
|
||||
throw missingVariable(rawExpression, variableName, e)
|
||||
}
|
||||
throw resolveFailed(expressionKey, rawExpression, e)
|
||||
}
|
||||
|
||||
val convertedValue = if (fieldType.isTypeValid(result)) {
|
||||
result as T
|
||||
} else {
|
||||
safeConvert(expressionKey, rawExpression, converter, result, fieldType)
|
||||
?: throw invalidValue(expressionKey, rawExpression, result)
|
||||
}
|
||||
|
||||
safeValidate(expressionKey, rawExpression, validator, convertedValue)
|
||||
|
||||
return convertedValue
|
||||
}
|
||||
|
||||
private fun <R : Any> getEvaluationResult(
|
||||
rawExpression: String,
|
||||
evaluable: Evaluable
|
||||
): R {
|
||||
val cachedValue = evaluationsCache[rawExpression]
|
||||
if (cachedValue != null) {
|
||||
return cachedValue as R
|
||||
}
|
||||
|
||||
val value = evaluator.eval<R>(evaluable)
|
||||
if (evaluable.checkIsCacheable()) {
|
||||
evaluable.variables.forEach {
|
||||
val expressions = varToExpressions.getOrPut(it) { mutableSetOf() }
|
||||
expressions.add(rawExpression)
|
||||
}
|
||||
evaluationsCache[rawExpression] = value
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
private fun tryGetMissingVariableName(e: EvaluableException): String? {
|
||||
return if (e is MissingVariableException) e.variableName else null
|
||||
}
|
||||
|
||||
private fun <R, T> safeConvert(
|
||||
expressionKey: String,
|
||||
rawExpression: String,
|
||||
converter: Converter<R, T?>?,
|
||||
rawValue: R,
|
||||
fieldType: TypeHelper<T>
|
||||
): T? {
|
||||
val convertedValue = if (converter == null) {
|
||||
// cast is unsafe but we hope that safeValidate() will check this type later
|
||||
rawValue as? T
|
||||
} else {
|
||||
try {
|
||||
// We need to guard with try..catch because at this point we're
|
||||
// not sure that resolved expression type is same type that converter accepts.
|
||||
converter.invoke(rawValue)
|
||||
} catch (e: ClassCastException) {
|
||||
throw typeMismatch(
|
||||
expressionKey = expressionKey,
|
||||
rawExpression = rawExpression,
|
||||
wrongTypeValue = rawValue,
|
||||
cause = e,
|
||||
)
|
||||
} catch (e: Exception) {
|
||||
// okay to catch exceptions here cause convertors may
|
||||
// throw different types of errors from common ClassCastException
|
||||
// up to specific NumberFormatException.
|
||||
throw invalidValue(
|
||||
expressionKey = expressionKey,
|
||||
rawExpression = rawExpression,
|
||||
wrongValue = rawValue,
|
||||
cause = e,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun fieldAwaitsStringButValueNotConverted(value: T?): Boolean {
|
||||
if (value == null) {
|
||||
return false
|
||||
}
|
||||
return fieldType.typeDefault is String && !fieldType.isTypeValid(value)
|
||||
}
|
||||
|
||||
return if (fieldAwaitsStringButValueNotConverted(convertedValue)) {
|
||||
convertedValue.toString() as T
|
||||
} else {
|
||||
convertedValue
|
||||
}
|
||||
}
|
||||
|
||||
private fun <T> safeValidate(
|
||||
expressionKey: String,
|
||||
rawExpression: String,
|
||||
validator: ValueValidator<T>,
|
||||
convertedValue: T
|
||||
) {
|
||||
try {
|
||||
// We need to guard with try..catch because at this point we're
|
||||
// not sure that resolved expression type is same type that validator checks.
|
||||
if (!validator.isValid(convertedValue)) {
|
||||
throw invalidValue(rawExpression, convertedValue)
|
||||
}
|
||||
} catch (e: ClassCastException) {
|
||||
throw typeMismatch(
|
||||
expressionKey = expressionKey,
|
||||
rawExpression = rawExpression,
|
||||
wrongTypeValue = convertedValue,
|
||||
cause = e,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
package com.yandex.div.compose.expressions
|
||||
|
||||
import com.yandex.div.compose.DivReporter
|
||||
import com.yandex.div.compose.dagger.DivContextScope
|
||||
import com.yandex.div.evaluable.ExpressionContext
|
||||
import com.yandex.div.evaluable.WarningSender
|
||||
import javax.inject.Inject
|
||||
|
||||
@DivContextScope
|
||||
internal class EvaluatorWarningSender @Inject constructor(
|
||||
private val reporter: DivReporter
|
||||
): WarningSender {
|
||||
|
||||
override fun send(
|
||||
expressionContext: ExpressionContext,
|
||||
message: String
|
||||
) {
|
||||
reporter.reportWarning(message)
|
||||
}
|
||||
}
|
||||
@@ -15,6 +15,6 @@ internal fun DivBlockView(
|
||||
is Div.Container -> DivContainerView(modifier, data.value)
|
||||
is Div.Image -> DivImageView(modifier, data.value)
|
||||
is Div.Text -> DivTextView(modifier, data.value)
|
||||
else -> TODO()
|
||||
else -> reporter.reportError("Element not supported")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.yandex.div.compose.DivContext
|
||||
import com.yandex.div.compose.DivReporter
|
||||
import com.yandex.div.json.expressions.Expression
|
||||
import com.yandex.div.json.expressions.ExpressionResolver
|
||||
|
||||
@@ -25,6 +26,10 @@ internal val expressionResolver: ExpressionResolver
|
||||
@Composable
|
||||
get() = divContext.expressionResolver
|
||||
|
||||
internal val reporter: DivReporter
|
||||
@Composable
|
||||
get() = divContext.reporter
|
||||
|
||||
@Composable
|
||||
internal fun <T : Any> Expression<T>.evaluate(): T {
|
||||
return evaluate(expressionResolver)
|
||||
|
||||
+16
-4
@@ -4,8 +4,13 @@ import androidx.compose.foundation.background
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import com.yandex.div.compose.views.evaluate
|
||||
import com.yandex.div.compose.views.reporter
|
||||
import com.yandex.div.compose.views.toColor
|
||||
import com.yandex.div2.DivBackground
|
||||
import com.yandex.div2.DivImageBackground
|
||||
import com.yandex.div2.DivLinearGradient
|
||||
import com.yandex.div2.DivNinePatchBackground
|
||||
import com.yandex.div2.DivRadialGradient
|
||||
import com.yandex.div2.DivSolidBackground
|
||||
|
||||
@Composable
|
||||
@@ -16,10 +21,17 @@ internal fun Modifier.backgrounds(value: List<DivBackground>): Modifier {
|
||||
is DivBackground.Solid ->
|
||||
modifier = modifier.solidBackground(background.value)
|
||||
|
||||
is DivBackground.Image -> TODO()
|
||||
is DivBackground.LinearGradient -> TODO()
|
||||
is DivBackground.NinePatch -> TODO()
|
||||
is DivBackground.RadialGradient -> TODO()
|
||||
is DivBackground.Image ->
|
||||
reporter.reportError("Background not supported: ${DivImageBackground.TYPE}")
|
||||
|
||||
is DivBackground.LinearGradient ->
|
||||
reporter.reportError("Background not supported: ${DivLinearGradient.TYPE}")
|
||||
|
||||
is DivBackground.NinePatch ->
|
||||
reporter.reportError("Background not supported: ${DivNinePatchBackground.TYPE}")
|
||||
|
||||
is DivBackground.RadialGradient ->
|
||||
reporter.reportError("Background not supported: ${DivRadialGradient.TYPE}")
|
||||
}
|
||||
}
|
||||
return modifier
|
||||
|
||||
+100
@@ -0,0 +1,100 @@
|
||||
package com.yandex.div.compose.expressions
|
||||
|
||||
import com.yandex.div.compose.DivReporter
|
||||
import com.yandex.div.data.Variable
|
||||
import com.yandex.div.evaluable.EvaluationContext
|
||||
import com.yandex.div.evaluable.Evaluator
|
||||
import com.yandex.div.evaluable.function.GeneratedBuiltinFunctionProvider
|
||||
import com.yandex.div.internal.parser.TYPE_HELPER_STRING
|
||||
import com.yandex.div.internal.parser.TypeHelper
|
||||
import com.yandex.div.json.expressions.Expression
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.fail
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.mockito.kotlin.mock
|
||||
import org.robolectric.RobolectricTestRunner
|
||||
|
||||
@RunWith(RobolectricTestRunner::class)
|
||||
class DivComposeExpressionResolverTest {
|
||||
|
||||
private val variables = mutableMapOf<String, Variable>()
|
||||
|
||||
private val evaluationContext = EvaluationContext(
|
||||
variableProvider = { variables[it]?.getValue() },
|
||||
storedValueProvider = mock(),
|
||||
functionProvider = GeneratedBuiltinFunctionProvider,
|
||||
warningSender = { _, _ -> }
|
||||
)
|
||||
|
||||
private val reporter = object : DivReporter() {
|
||||
override fun reportError(message: String) {
|
||||
fail(message)
|
||||
}
|
||||
|
||||
override fun reportError(message: String, e: Throwable) {
|
||||
fail(message)
|
||||
}
|
||||
}
|
||||
|
||||
private val expressionResolver = DivComposeExpressionResolver(
|
||||
Evaluator(evaluationContext),
|
||||
reporter
|
||||
)
|
||||
|
||||
@Test
|
||||
fun `expression with string function`() {
|
||||
assertEquals(
|
||||
"length = 9",
|
||||
evaluate("length = @{len('123456789')}")
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `expression with string variables`() {
|
||||
variables["host"] = Variable.StringVariable("host", "test.abc")
|
||||
variables["path"] = Variable.StringVariable("path", "path")
|
||||
|
||||
assertEquals(
|
||||
"https://test.abc/path",
|
||||
evaluate("https://@{host}/@{path}")
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `expression with integer variable`() {
|
||||
variables["value"] = Variable.IntegerVariable("value", 100)
|
||||
|
||||
assertEquals(
|
||||
"value + 10 = 110",
|
||||
evaluate("value + 10 = @{value + 10}")
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `expression with double variable`() {
|
||||
variables["value"] = Variable.DoubleVariable("value", 123.45)
|
||||
|
||||
assertEquals(
|
||||
"value + 10 = 133.45",
|
||||
evaluate("value + 10 = @{value + 10}")
|
||||
)
|
||||
}
|
||||
|
||||
private fun evaluate(expression: String): String {
|
||||
return expression(expression, TYPE_HELPER_STRING).evaluate(expressionResolver)
|
||||
}
|
||||
|
||||
private fun <T : Any> expression(
|
||||
rawExpression: String,
|
||||
typeHelper: TypeHelper<T>,
|
||||
validator: (T) -> Boolean = { true },
|
||||
) = Expression.MutableExpression<T, T>(
|
||||
expressionKey = "test",
|
||||
rawExpression = rawExpression,
|
||||
validator = validator,
|
||||
converter = { it },
|
||||
logger = { fail(it.message) },
|
||||
typeHelper = typeHelper,
|
||||
)
|
||||
}
|
||||
+1
@@ -6,6 +6,7 @@ import androidx.activity.ComponentActivity
|
||||
import androidx.compose.ui.platform.ComposeView
|
||||
import com.yandex.div.compose.DivComposeConfiguration
|
||||
import com.yandex.div.compose.DivView
|
||||
import com.yandex.div.compose.createContext
|
||||
import com.yandex.div.data.DivParsingEnvironment
|
||||
import com.yandex.div.json.ParsingErrorLogger
|
||||
import com.yandex.div2.DivData
|
||||
|
||||
Reference in New Issue
Block a user