1
mirror of https://github.com/revanced/revanced-patcher synced 2025-09-06 16:38:50 +02:00

Compare commits

...

6 Commits

Author SHA1 Message Date
semantic-release-bot
2aee0cbd0f chore(release): 3.1.0 [skip ci]
# [3.1.0](https://github.com/revanced/revanced-patcher/compare/v3.0.0...v3.1.0) (2022-08-02)

### Features

* validator for patch options ([4e2e772](4e2e772389))
2022-08-02 20:02:18 +00:00
Sculas
19256b5437 Merge remote-tracking branch 'origin/main' into main 2022-08-02 22:00:40 +02:00
Sculas
67a5237541 test: refactor & add more tests 2022-08-02 22:00:32 +02:00
Sculas
4e2e772389 feat: validator for patch options 2022-08-02 22:00:10 +02:00
semantic-release-bot
799bc9e163 chore(release): 3.0.0 [skip ci]
# [3.0.0](https://github.com/revanced/revanced-patcher/compare/v2.9.0...v3.0.0) (2022-08-02)

### Features

* registry for patch options ([2431785](2431785d0e))

### BREAKING CHANGES

* Patch options now use the PatchOptions registry class instead of an Iterable. This change requires modifications to existing patches using this API.
2022-08-02 19:11:43 +00:00
Sculas
2431785d0e 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.
2022-08-02 21:10:14 +02:00
7 changed files with 152 additions and 48 deletions

View File

@@ -1,3 +1,22 @@
# [3.1.0](https://github.com/revanced/revanced-patcher/compare/v3.0.0...v3.1.0) (2022-08-02)
### Features
* validator for patch options ([4e2e772](https://github.com/revanced/revanced-patcher/commit/4e2e77238957d7732326cfe5e05145bf7dab5bfb))
# [3.0.0](https://github.com/revanced/revanced-patcher/compare/v2.9.0...v3.0.0) (2022-08-02)
### Features
* registry for patch options ([2431785](https://github.com/revanced/revanced-patcher/commit/2431785d0e494d6271c6951eec9adfff9db95c17))
### BREAKING CHANGES
* Patch options now use the PatchOptions registry class instead of an Iterable. This change requires modifications to existing patches using this API.
# [2.9.0](https://github.com/revanced/revanced-patcher/compare/v2.8.0...v2.9.0) (2022-08-02)

View File

@@ -1,2 +1,2 @@
kotlin.code.style = official
version = 2.9.0
version = 3.1.0

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`_
@@ -14,9 +55,16 @@ sealed class PatchOption<T>(
default: T?,
val title: String,
val description: String,
val required: Boolean
val required: Boolean,
val validator: (T?) -> Boolean
) {
var value: T? = default
set(value) {
if (!validator(value)) {
throw IllegalArgumentException("Illegal value: $value")
}
field = value
}
/**
* A [PatchOption] representing a [String].
@@ -27,9 +75,10 @@ sealed class PatchOption<T>(
default: String?,
title: String,
description: String,
required: Boolean = false
required: Boolean = false,
validator: (String?) -> Boolean = { true }
) : PatchOption<String>(
key, default, title, description, required
key, default, title, description, required, validator
)
/**
@@ -41,9 +90,10 @@ sealed class PatchOption<T>(
default: Boolean?,
title: String,
description: String,
required: Boolean = false
required: Boolean = false,
validator: (Boolean?) -> Boolean = { true }
) : PatchOption<Boolean>(
key, default, title, description, required
key, default, title, description, required, validator
)
/**
@@ -57,9 +107,12 @@ sealed class PatchOption<T>(
val options: Iterable<E>,
title: String,
description: String,
required: Boolean = false
required: Boolean = false,
validator: (E?) -> Boolean = { true }
) : PatchOption<E>(
key, default, title, description, required
key, default, title, description, required, {
(it?.let { it in options } ?: true) && validator(it)
}
) {
init {
if (default !in options) {
@@ -78,9 +131,10 @@ sealed class PatchOption<T>(
options: Iterable<String>,
title: String,
description: String,
required: Boolean = false
required: Boolean = false,
validator: (String?) -> Boolean = { true }
) : ListOption<String>(
key, default, options, title, description, required
key, default, options, title, description, required, validator
)
/**
@@ -93,8 +147,9 @@ sealed class PatchOption<T>(
options: Iterable<Int>,
title: String,
description: String,
required: Boolean = false
required: Boolean = false,
validator: (Int?) -> Boolean = { true }
) : ListOption<Int>(
key, default, options, title, description, required
key, default, options, title, description, required, validator
)
}

View File

@@ -0,0 +1,59 @@
package app.revanced.patcher.patch
import app.revanced.patcher.usage.bytecode.ExampleBytecodePatch
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.assertThrows
internal class PatchOptionsTest {
private val options = ExampleBytecodePatch().options
@Test
fun `should not throw an exception`() {
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)
}
}
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)
}
@Test
fun `should fail because the option does not exist`() {
assertThrows<NoSuchOptionException> {
options["this option does not exist"] = 123
}
}
@Test
fun `should fail because of invalid value type`() {
assertThrows<IllegalArgumentException> {
options["key1"] = 123
}
}
@Test
fun `should fail because of an illegal value`() {
assertThrows<IllegalArgumentException> {
options["key3"] = "this value is not an allowed option"
}
}
}

View File

@@ -1,30 +0,0 @@
package app.revanced.patcher.usage
import app.revanced.patcher.patch.PatchOption
import app.revanced.patcher.usage.bytecode.ExampleBytecodePatch
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)
}
}
is PatchOption.IntListOption -> {
option.value = option.options.first()
for (choice in option.options) {
println(choice)
}
}
}
}
}

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"
),
)
}