feat: registry for patch options

BREAKING CHANGE: Patch options now use the PatchOptions registry class instead of an Iterable. This change requires modifications to existing patches using this API.
This commit is contained in:
Sculas 2022-08-02 21:10:14 +02:00
parent fb3c0e87d4
commit 2431785d0e
No known key found for this signature in database
GPG key ID: 1530BFF96D1EEB89
4 changed files with 74 additions and 25 deletions

View file

@ -21,5 +21,5 @@ abstract class Patch<out T : Data> {
/**
* A list of [PatchOption]s.
*/
open val options: Iterable<PatchOption<*>> = listOf()
open val options = PatchOptions()
}

View file

@ -1,5 +1,46 @@
package app.revanced.patcher.patch
@Suppress("CanBeParameter", "MemberVisibilityCanBePrivate")
class NoSuchOptionException(val option: String) : Exception("No such option: $option")
/**
* A registry for an array of [PatchOption]s.
* @param options An array of [PatchOption]s.
*/
@Suppress("MemberVisibilityCanBePrivate")
class PatchOptions(vararg val options: PatchOption<*>) : Iterable<PatchOption<*>> {
private val register = buildMap {
for (option in options) {
if (containsKey(option.key)) {
throw IllegalStateException("Multiple options found with the same key")
}
put(option.key, option)
}
}
/**
* Get a [PatchOption] by its key.
* @param key The key of the [PatchOption].
*/
operator fun get(key: String) = register[key] ?: throw NoSuchOptionException(key)
/**
* Set the value of a [PatchOption].
* @param key The key of the [PatchOption].
* @param value The value you want it to be.
* Please note that using the wrong value type results in a runtime error.
*/
inline operator fun <reified T> set(key: String, value: T) {
@Suppress("UNCHECKED_CAST") val opt = get(key) as? PatchOption<T>
if (opt == null || opt.value !is T) throw IllegalArgumentException(
"The type of the option value is not the same as the type value provided"
)
opt.value = value
}
override fun iterator() = options.iterator()
}
/**
* A [Patch] option.
* @param key Unique identifier of the option. Example: _`settings.microg.enabled`_

View file

@ -2,29 +2,36 @@ package app.revanced.patcher.usage
import app.revanced.patcher.patch.PatchOption
import app.revanced.patcher.usage.bytecode.ExampleBytecodePatch
import kotlin.test.Test
fun patchOptionsUsage() {
val options = ExampleBytecodePatch().options
for (option in options) {
when (option) {
is PatchOption.StringOption -> {
option.value = "Hello World"
}
is PatchOption.BooleanOption -> {
option.value = false
}
is PatchOption.StringListOption -> {
option.value = option.options.first()
for (choice in option.options) {
println(choice)
internal class PatchOptionsUsage {
@Test
fun patchOptionsUsage() {
val options = ExampleBytecodePatch().options
for (option in options) {
when (option) {
is PatchOption.StringOption -> {
option.value = "Hello World"
}
}
is PatchOption.IntListOption -> {
option.value = option.options.first()
for (choice in option.options) {
println(choice)
is PatchOption.BooleanOption -> {
option.value = false
}
is PatchOption.StringListOption -> {
option.value = option.options.first()
for (choice in option.options) {
println(choice)
}
}
is PatchOption.IntListOption -> {
option.value = option.options.first()
for (choice in option.options) {
println(choice)
}
}
}
}
println(options["key1"].value)
options["key1"] = "Hello, world!"
println(options["key1"].value)
}
}

View file

@ -8,6 +8,7 @@ import app.revanced.patcher.extensions.addInstructions
import app.revanced.patcher.extensions.or
import app.revanced.patcher.extensions.replaceInstruction
import app.revanced.patcher.patch.PatchOption
import app.revanced.patcher.patch.PatchOptions
import app.revanced.patcher.patch.PatchResult
import app.revanced.patcher.patch.PatchResultSuccess
import app.revanced.patcher.patch.annotations.DependencyType
@ -164,18 +165,18 @@ class ExampleBytecodePatch : BytecodePatch(listOf(ExampleFingerprint)) {
)
}
override val options = listOf(
override val options = PatchOptions(
PatchOption.StringOption(
"key", "default", "title", "description", true
"key1", "default", "title", "description", true
),
PatchOption.BooleanOption(
"key", true, "title", "description" // required defaults to false
"key2", true, "title", "description" // required defaults to false
),
PatchOption.StringListOption(
"key", "TEST", listOf("TEST", "TEST1", "TEST2"), "title", "description"
"key3", "TEST", listOf("TEST", "TEST1", "TEST2"), "title", "description"
),
PatchOption.IntListOption(
"key", 1, listOf(1, 2, 3), "title", "description"
"key4", 1, listOf(1, 2, 3), "title", "description"
),
)
}