Skip to content

Commit

Permalink
feat: Add PatchOption#valueType to handle type erasure
Browse files Browse the repository at this point in the history
Without this new property, it is not possible to infer the type without abusing `ClassCastException` at runtime to infer the type of the option value.

BREAKING CHANGE: This changes the signature of the `PatchOption` constructor.
  • Loading branch information
oSumAtrIX committed Oct 24, 2023
1 parent dc09ea6 commit a46e948
Show file tree
Hide file tree
Showing 3 changed files with 38 additions and 11 deletions.
3 changes: 2 additions & 1 deletion api/revanced-patcher.api
Expand Up @@ -289,7 +289,7 @@ public abstract interface annotation class app/revanced/patcher/patch/annotation

public class app/revanced/patcher/patch/options/PatchOption {
public static final field PatchExtensions Lapp/revanced/patcher/patch/options/PatchOption$PatchExtensions;
public fun <init> (Ljava/lang/String;Ljava/lang/Object;Ljava/util/Map;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;)V
public fun <init> (Ljava/lang/String;Ljava/lang/Object;Ljava/util/Map;Ljava/lang/String;Ljava/lang/String;ZLjava/lang/String;Lkotlin/jvm/functions/Function2;)V
public final fun getDefault ()Ljava/lang/Object;
public final fun getDescription ()Ljava/lang/String;
public final fun getKey ()Ljava/lang/String;
Expand All @@ -298,6 +298,7 @@ public class app/revanced/patcher/patch/options/PatchOption {
public final fun getValidator ()Lkotlin/jvm/functions/Function2;
public final fun getValue ()Ljava/lang/Object;
public final fun getValue (Ljava/lang/Object;Lkotlin/reflect/KProperty;)Ljava/lang/Object;
public final fun getValueType ()Ljava/lang/String;
public final fun getValues ()Ljava/util/Map;
public fun reset ()V
public final fun setValue (Ljava/lang/Object;)V
Expand Down
42 changes: 32 additions & 10 deletions src/main/kotlin/app/revanced/patcher/patch/options/PatchOption.kt
Expand Up @@ -12,6 +12,7 @@ import kotlin.reflect.KProperty
* @param title The title.
* @param description A description.
* @param required Whether the option is required.
* @param valueType The type of the option value (to handle type erasure).
* @param validator The function to validate the option value.
* @param T The value type of the option.
*/
Expand All @@ -23,6 +24,7 @@ open class PatchOption<T>(
val title: String?,
val description: String?,
val required: Boolean,
val valueType: String,
val validator: PatchOption<T>.(T?) -> Boolean
) {
/**
Expand Down Expand Up @@ -110,7 +112,9 @@ open class PatchOption<T>(
description: String? = null,
required: Boolean = false,
validator: PatchOption<String>.(String?) -> Boolean = { true }
) = PatchOption(key, default, values, title, description, required, validator).also { registerOption(it) }
) = PatchOption(
key, default, values, title, description, required, "String", validator
).also { registerOption(it) }

/**
* Create a new [PatchOption] with an integer value and add it to the current [Patch].
Expand All @@ -135,7 +139,9 @@ open class PatchOption<T>(
description: String? = null,
required: Boolean = false,
validator: PatchOption<Int?>.(Int?) -> Boolean = { true }
) = PatchOption(key, default, values, title, description, required, validator).also { registerOption(it) }
) = PatchOption(
key, default, values, title, description, required, "Int", validator
).also { registerOption(it) }

/**
* Create a new [PatchOption] with a boolean value and add it to the current [Patch].
Expand All @@ -160,7 +166,9 @@ open class PatchOption<T>(
description: String? = null,
required: Boolean = false,
validator: PatchOption<Boolean?>.(Boolean?) -> Boolean = { true }
) = PatchOption(key, default, values, title, description, required, validator).also { registerOption(it) }
) = PatchOption(key, default, values, title, description, required, "Boolean", validator).also {
registerOption(it)
}

/**
* Create a new [PatchOption] with a float value and add it to the current [Patch].
Expand All @@ -185,7 +193,9 @@ open class PatchOption<T>(
description: String? = null,
required: Boolean = false,
validator: PatchOption<Float?>.(Float?) -> Boolean = { true }
) = PatchOption(key, default, values, title, description, required, validator).also { registerOption(it) }
) = PatchOption(
key, default, values, title, description, required, "Float", validator
).also { registerOption(it) }

/**
* Create a new [PatchOption] with a long value and add it to the current [Patch].
Expand All @@ -210,7 +220,9 @@ open class PatchOption<T>(
description: String? = null,
required: Boolean = false,
validator: PatchOption<Long?>.(Long?) -> Boolean = { true }
) = PatchOption(key, default, values, title, description, required, validator).also { registerOption(it) }
) = PatchOption(
key, default, values, title, description, required, "Long", validator
).also { registerOption(it) }

/**
* Create a new [PatchOption] with a string array value and add it to the current [Patch].
Expand All @@ -235,7 +247,9 @@ open class PatchOption<T>(
description: String? = null,
required: Boolean = false,
validator: PatchOption<Array<String>?>.(Array<String>?) -> Boolean = { true }
) = PatchOption(key, default, values, title, description, required, validator).also { registerOption(it) }
) = PatchOption(
key, default, values, title, description, required, "StringArray", validator
).also { registerOption(it) }

/**
* Create a new [PatchOption] with an integer array value and add it to the current [Patch].
Expand All @@ -260,7 +274,9 @@ open class PatchOption<T>(
description: String? = null,
required: Boolean = false,
validator: PatchOption<Array<Int>?>.(Array<Int>?) -> Boolean = { true }
) = PatchOption(key, default, values, title, description, required, validator).also { registerOption(it) }
) = PatchOption(
key, default, values, title, description, required, "IntArray", validator
).also { registerOption(it) }

/**
* Create a new [PatchOption] with a boolean array value and add it to the current [Patch].
Expand All @@ -285,7 +301,9 @@ open class PatchOption<T>(
description: String? = null,
required: Boolean = false,
validator: PatchOption<Array<Boolean>?>.(Array<Boolean>?) -> Boolean = { true }
) = PatchOption(key, default, values, title, description, required, validator).also { registerOption(it) }
) = PatchOption(
key, default, values, title, description, required, "BooleanArray", validator
).also { registerOption(it) }

/**
* Create a new [PatchOption] with a float array value and add it to the current [Patch].
Expand All @@ -310,7 +328,9 @@ open class PatchOption<T>(
description: String? = null,
required: Boolean = false,
validator: PatchOption<Array<Float>?>.(Array<Float>?) -> Boolean = { true }
) = PatchOption(key, default, values, title, description, required, validator).also { registerOption(it) }
) = PatchOption(
key, default, values, title, description, required, "FloatArray", validator
).also { registerOption(it) }

/**
* Create a new [PatchOption] with a long array value and add it to the current [Patch].
Expand All @@ -335,7 +355,9 @@ open class PatchOption<T>(
description: String? = null,
required: Boolean = false,
validator: PatchOption<Array<Long>?>.(Array<Long>?) -> Boolean = { true }
) = PatchOption(key, default, values, title, description, required, validator).also { registerOption(it) }
) = PatchOption(
key, default, values, title, description, required, "LongArray", validator
).also { registerOption(it) }

private fun <P : Patch<*>> P.registerOption(option: PatchOption<*>) = option.also { options.register(it) }
}
Expand Down
Expand Up @@ -85,6 +85,10 @@ internal class PatchOptionsTest {
}
}

@Test
fun `option types should be known`() =
assertTrue(OptionsTestPatch.options["array"].valueType == "StringArray")

@Test
fun `getting default value should work`() =
assertDoesNotThrow { assertNull(OptionsTestPatch.resettableOption.default) }
Expand Down

0 comments on commit a46e948

Please sign in to comment.