From 2431785d0e494d6271c6951eec9adfff9db95c17 Mon Sep 17 00:00:00 2001 From: Sculas Date: Tue, 2 Aug 2022 21:10:14 +0200 Subject: [PATCH] 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. --- .../app/revanced/patcher/patch/Patch.kt | 2 +- .../app/revanced/patcher/patch/PatchOption.kt | 41 +++++++++++++++++ .../patcher/usage/PatchOptionsUsage.kt | 45 +++++++++++-------- .../usage/bytecode/ExampleBytecodePatch.kt | 11 ++--- 4 files changed, 74 insertions(+), 25 deletions(-) diff --git a/src/main/kotlin/app/revanced/patcher/patch/Patch.kt b/src/main/kotlin/app/revanced/patcher/patch/Patch.kt index 53a3609..264cdaa 100644 --- a/src/main/kotlin/app/revanced/patcher/patch/Patch.kt +++ b/src/main/kotlin/app/revanced/patcher/patch/Patch.kt @@ -21,5 +21,5 @@ abstract class Patch { /** * A list of [PatchOption]s. */ - open val options: Iterable> = listOf() + open val options = PatchOptions() } \ No newline at end of file diff --git a/src/main/kotlin/app/revanced/patcher/patch/PatchOption.kt b/src/main/kotlin/app/revanced/patcher/patch/PatchOption.kt index 216e881..c61effe 100644 --- a/src/main/kotlin/app/revanced/patcher/patch/PatchOption.kt +++ b/src/main/kotlin/app/revanced/patcher/patch/PatchOption.kt @@ -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> { + 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 set(key: String, value: T) { + @Suppress("UNCHECKED_CAST") val opt = get(key) as? PatchOption + 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`_ diff --git a/src/test/kotlin/app/revanced/patcher/usage/PatchOptionsUsage.kt b/src/test/kotlin/app/revanced/patcher/usage/PatchOptionsUsage.kt index 2828e45..6c213c2 100644 --- a/src/test/kotlin/app/revanced/patcher/usage/PatchOptionsUsage.kt +++ b/src/test/kotlin/app/revanced/patcher/usage/PatchOptionsUsage.kt @@ -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) } } \ No newline at end of file diff --git a/src/test/kotlin/app/revanced/patcher/usage/bytecode/ExampleBytecodePatch.kt b/src/test/kotlin/app/revanced/patcher/usage/bytecode/ExampleBytecodePatch.kt index 0a21d5e..29828f6 100644 --- a/src/test/kotlin/app/revanced/patcher/usage/bytecode/ExampleBytecodePatch.kt +++ b/src/test/kotlin/app/revanced/patcher/usage/bytecode/ExampleBytecodePatch.kt @@ -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" ), ) }