mirror of
https://github.com/ReVanced/revanced-library.git
synced 2026-01-20 01:43:57 +00:00
refactor: Move functions to top level
This commit is contained in:
@@ -8,7 +8,32 @@ import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
|
||||
import java.io.File
|
||||
import java.util.logging.Logger
|
||||
|
||||
private val logger = Logger.getLogger("Options")
|
||||
|
||||
typealias PatchName = String
|
||||
typealias OptionKey = String
|
||||
typealias OptionValue = Any?
|
||||
typealias PatchesOptions = Map<PatchName, Map<OptionKey, OptionValue>>
|
||||
|
||||
/**
|
||||
* Set the options for a set of patches that have a name.
|
||||
*
|
||||
* @param options The options to set. The key is the patch name and the value is a map of option keys to option values.
|
||||
*/
|
||||
fun Set<Patch<*>>.setOptions(options: PatchesOptions) = filter { it.name != null }.forEach { patch ->
|
||||
val patchOptions = options[patch.name] ?: return@forEach
|
||||
|
||||
patch.options.forEach option@{ option ->
|
||||
try {
|
||||
patch.options[option.key] = patchOptions[option.key] ?: return@option
|
||||
} catch (e: OptionException) {
|
||||
logger.warning("Could not set option value for the \"${patch.name}\" patch: ${e.message}")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("unused")
|
||||
@Deprecated("Functions have been moved to top level.")
|
||||
object Options {
|
||||
private val logger = Logger.getLogger(Options::class.java.name)
|
||||
|
||||
@@ -21,6 +46,7 @@ object Options {
|
||||
* @param prettyPrint Whether to pretty print the JSON.
|
||||
* @return The JSON string containing the options.
|
||||
*/
|
||||
@Deprecated("Functions have been moved to the Serialization class.")
|
||||
fun serialize(
|
||||
patches: Set<app.revanced.patcher.patch.Patch<*>>,
|
||||
prettyPrint: Boolean = false,
|
||||
@@ -35,7 +61,7 @@ object Options {
|
||||
try {
|
||||
option.value
|
||||
} catch (e: OptionException) {
|
||||
logger.warning("Using default option value for the ${patch.name} patch: ${e.message}")
|
||||
logger.warning("Using default option value for the \"${patch.name}\" patch: ${e.message}")
|
||||
option.default
|
||||
}
|
||||
|
||||
@@ -60,6 +86,7 @@ object Options {
|
||||
* @return A set of [Patch]s.
|
||||
* @see Patch
|
||||
*/
|
||||
@Deprecated("Functions have been moved to the Serialization class.")
|
||||
fun deserialize(json: String): Array<Patch> = mapper.readValue(json, Array<Patch>::class.java)
|
||||
|
||||
/**
|
||||
@@ -67,26 +94,16 @@ object Options {
|
||||
*
|
||||
* @param json The JSON string containing the options.
|
||||
*/
|
||||
@Deprecated("Function has been moved to top level.")
|
||||
fun Set<app.revanced.patcher.patch.Patch<*>>.setOptions(json: String) {
|
||||
filter { it.options.any() }.let { patches ->
|
||||
if (patches.isEmpty()) return
|
||||
|
||||
val jsonPatches =
|
||||
deserialize(json).associate {
|
||||
it.patchName to it.options.associate { option -> option.key to option.value }
|
||||
}
|
||||
|
||||
patches.forEach { patch ->
|
||||
jsonPatches[patch.name]?.let { jsonPatchOptions ->
|
||||
jsonPatchOptions.forEach { (option, value) ->
|
||||
try {
|
||||
patch.options[option] = value
|
||||
} catch (e: OptionException) {
|
||||
logger.warning("Could not set option value for the ${patch.name} patch: ${e.message}")
|
||||
}
|
||||
}
|
||||
}
|
||||
val jsonPatches = deserialize(json).associate {
|
||||
it.patchName to it.options.associate { option -> option.key to option.value }
|
||||
}
|
||||
|
||||
setOptions(jsonPatches)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -96,6 +113,7 @@ object Options {
|
||||
* @param file The file containing the JSON string containing the options.
|
||||
* @see setOptions
|
||||
*/
|
||||
@Deprecated("Function has been moved to top level.")
|
||||
fun Set<app.revanced.patcher.patch.Patch<*>>.setOptions(file: File) = setOptions(file.readText())
|
||||
|
||||
/**
|
||||
|
||||
52
src/commonMain/kotlin/app/revanced/library/Patch.kt
Normal file
52
src/commonMain/kotlin/app/revanced/library/Patch.kt
Normal file
@@ -0,0 +1,52 @@
|
||||
package app.revanced.library
|
||||
|
||||
import app.revanced.patcher.patch.Package
|
||||
import app.revanced.patcher.patch.Patch
|
||||
|
||||
typealias PackageName = String
|
||||
typealias Version = String
|
||||
typealias Count = Int
|
||||
|
||||
typealias VersionMap = LinkedHashMap<Version, Count>
|
||||
typealias PackageNameMap = Map<PackageName, VersionMap>
|
||||
|
||||
/**
|
||||
* Get the count of versions for each compatible package from the set of [Patch] ordered by the most common version.
|
||||
*
|
||||
* @param packageNames The names of the compatible packages to include. If null, all packages will be included.
|
||||
* @param countUnusedPatches Whether to count patches that are not used.
|
||||
* @return A map of package names to a map of versions to their count.
|
||||
*/
|
||||
fun Set<Patch<*>>.mostCommonCompatibleVersions(
|
||||
packageNames: Set<String>? = null,
|
||||
countUnusedPatches: Boolean = false,
|
||||
): PackageNameMap = buildMap {
|
||||
fun filterWantedPackages(compatiblePackages: List<Package>): List<Package> {
|
||||
val wantedPackages = packageNames?.toHashSet() ?: return compatiblePackages
|
||||
return compatiblePackages.filter { (name, _) -> name in wantedPackages }
|
||||
}
|
||||
|
||||
this@mostCommonCompatibleVersions.filter { it.use || countUnusedPatches }
|
||||
.flatMap { it.compatiblePackages ?: emptyList() }
|
||||
.let(::filterWantedPackages)
|
||||
.forEach { (name, versions) ->
|
||||
if (versions?.isEmpty() == true) {
|
||||
return@forEach
|
||||
}
|
||||
|
||||
val versionMap = getOrPut(name) { linkedMapOf() }
|
||||
|
||||
versions?.forEach { version ->
|
||||
versionMap[version] = versionMap.getOrDefault(version, 0) + 1
|
||||
}
|
||||
}
|
||||
|
||||
// Sort the version maps by the most common version.
|
||||
forEach { (packageName, versionMap) ->
|
||||
this[packageName] =
|
||||
versionMap
|
||||
.asIterable()
|
||||
.sortedWith(compareByDescending { it.value })
|
||||
.associate { it.key to it.value } as VersionMap
|
||||
}
|
||||
}
|
||||
@@ -8,63 +8,19 @@ import java.io.InputStream
|
||||
import java.io.OutputStream
|
||||
import kotlin.reflect.KType
|
||||
|
||||
typealias PackageName = String
|
||||
typealias Version = String
|
||||
typealias Count = Int
|
||||
|
||||
typealias VersionMap = LinkedHashMap<Version, Count>
|
||||
typealias PackageNameMap = Map<PackageName, VersionMap>
|
||||
|
||||
/**
|
||||
* Utility functions for working with patches.
|
||||
*/
|
||||
@Suppress("MemberVisibilityCanBePrivate", "unused")
|
||||
@Deprecated("Functions have been moved to top level.")
|
||||
object PatchUtils {
|
||||
/**
|
||||
* Get the count of versions for each compatible package from a supplied set of [patches] ordered by the most common version.
|
||||
*
|
||||
* @param patches The set of patches to check.
|
||||
* @param packageNames The names of the compatible packages to include. If null, all packages will be included.
|
||||
* @param countUnusedPatches Whether to count patches that are not used.
|
||||
* @return A map of package names to a map of versions to their count.
|
||||
*/
|
||||
@Deprecated(
|
||||
"Function has been moved to top level.",
|
||||
ReplaceWith("patches.mostCommonCompatibleVersions(packageNames, countUnusedPatches)"),
|
||||
)
|
||||
fun getMostCommonCompatibleVersions(
|
||||
patches: Set<Patch<*>>,
|
||||
packageNames: Set<String>? = null,
|
||||
countUnusedPatches: Boolean = false,
|
||||
): PackageNameMap =
|
||||
buildMap {
|
||||
fun filterWantedPackages(compatiblePackages: Iterable<Package>): Iterable<Package> {
|
||||
val wantedPackages = packageNames?.toHashSet() ?: return compatiblePackages
|
||||
return compatiblePackages.filter { (name, _) -> name in wantedPackages }
|
||||
}
|
||||
|
||||
patches
|
||||
.filter { it.use || countUnusedPatches }
|
||||
.flatMap { it.compatiblePackages ?: emptyList() }
|
||||
.let(::filterWantedPackages)
|
||||
.forEach { (name, versions) ->
|
||||
if (versions?.isEmpty() == true) {
|
||||
return@forEach
|
||||
}
|
||||
|
||||
val versionMap = getOrPut(name) { linkedMapOf() }
|
||||
|
||||
versions?.forEach { version ->
|
||||
versionMap[version] = versionMap.getOrDefault(version, 0) + 1
|
||||
}
|
||||
}
|
||||
|
||||
// Sort the version maps by the most common version.
|
||||
forEach { (packageName, versionMap) ->
|
||||
this[packageName] =
|
||||
versionMap
|
||||
.asIterable()
|
||||
.sortedWith(compareByDescending { it.value })
|
||||
.associate { it.key to it.value } as VersionMap
|
||||
}
|
||||
}
|
||||
): PackageNameMap = patches.mostCommonCompatibleVersions(packageNames, countUnusedPatches)
|
||||
|
||||
@Deprecated("Functions have been moved to the Serialization class.")
|
||||
object Json {
|
||||
private val mapper = jacksonObjectMapper()
|
||||
|
||||
@@ -76,6 +32,7 @@ object PatchUtils {
|
||||
* @param prettyPrint Whether to pretty print the JSON.
|
||||
* @param outputStream The output stream to write the JSON to.
|
||||
*/
|
||||
@Deprecated("Functions have been moved to the Serialization class.")
|
||||
fun serialize(
|
||||
patches: Set<Patch<*>>,
|
||||
transform: (Patch<*>) -> JsonPatch = { patch -> FullJsonPatch.fromPatch(patch) },
|
||||
@@ -99,6 +56,7 @@ object PatchUtils {
|
||||
* @return A set of [JsonPatch]es.
|
||||
* @see FullJsonPatch
|
||||
*/
|
||||
@Deprecated("This function will be removed in the future.")
|
||||
fun <T : JsonPatch> deserialize(
|
||||
inputStream: InputStream,
|
||||
jsonPatchElementClass: Class<T>,
|
||||
|
||||
114
src/commonMain/kotlin/app/revanced/library/Serialization.kt
Normal file
114
src/commonMain/kotlin/app/revanced/library/Serialization.kt
Normal file
@@ -0,0 +1,114 @@
|
||||
package app.revanced.library
|
||||
|
||||
import app.revanced.patcher.patch.*
|
||||
import kotlinx.serialization.*
|
||||
import kotlinx.serialization.builtins.*
|
||||
import kotlinx.serialization.descriptors.buildClassSerialDescriptor
|
||||
import kotlinx.serialization.descriptors.element
|
||||
import kotlinx.serialization.encoding.Decoder
|
||||
import kotlinx.serialization.encoding.Encoder
|
||||
import kotlinx.serialization.encoding.encodeStructure
|
||||
import kotlinx.serialization.json.*
|
||||
import java.io.OutputStream
|
||||
|
||||
private class PatchSerializer : KSerializer<Patch<*>> {
|
||||
override val descriptor = buildClassSerialDescriptor("Patch") {
|
||||
element<String?>("name")
|
||||
element<String?>("description")
|
||||
element<Boolean>("use")
|
||||
element<List<String>>("dependencies")
|
||||
element<Set<Package>?>("compatiblePackages")
|
||||
element("options", OptionSerializer.descriptor)
|
||||
}
|
||||
|
||||
override fun deserialize(decoder: Decoder) = throw NotImplementedError("Deserialization is unsupported")
|
||||
|
||||
@OptIn(ExperimentalSerializationApi::class)
|
||||
override fun serialize(encoder: Encoder, value: Patch<*>) {
|
||||
encoder.encodeStructure(descriptor) {
|
||||
encodeNullableSerializableElement(
|
||||
descriptor,
|
||||
0,
|
||||
String.serializer(),
|
||||
value.name,
|
||||
)
|
||||
encodeNullableSerializableElement(
|
||||
descriptor,
|
||||
1,
|
||||
String.serializer(),
|
||||
value.description,
|
||||
)
|
||||
encodeBooleanElement(
|
||||
descriptor,
|
||||
2,
|
||||
value.use,
|
||||
)
|
||||
encodeSerializableElement(
|
||||
descriptor,
|
||||
3,
|
||||
ListSerializer(String.serializer()),
|
||||
value.dependencies.map { it.name ?: it.toString() },
|
||||
)
|
||||
encodeNullableSerializableElement(
|
||||
descriptor,
|
||||
4,
|
||||
SetSerializer(PairSerializer(String.serializer(), SetSerializer(String.serializer()).nullable)),
|
||||
value.compatiblePackages,
|
||||
)
|
||||
encodeSerializableElement(
|
||||
descriptor,
|
||||
5,
|
||||
SetSerializer(OptionSerializer),
|
||||
value.options.values.toSet(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private object OptionSerializer : KSerializer<Option<*>> {
|
||||
override val descriptor = buildClassSerialDescriptor("Option") {
|
||||
element<String>("key")
|
||||
element<String?>("title")
|
||||
element<String?>("description")
|
||||
element<Boolean>("required")
|
||||
// Type does not matter for serialization. Using String.
|
||||
element<String>("type")
|
||||
element<String?>("default")
|
||||
// Map value type does not matter for serialization. Using String.
|
||||
element<Map<String, String?>?>("values")
|
||||
}
|
||||
|
||||
override fun deserialize(decoder: Decoder) = throw NotImplementedError("Deserialization is unsupported")
|
||||
|
||||
@OptIn(ExperimentalSerializationApi::class)
|
||||
override fun serialize(encoder: Encoder, value: Option<*>) {
|
||||
encoder.encodeStructure(descriptor) {
|
||||
encodeStringElement(descriptor, 0, value.key)
|
||||
encodeNullableSerializableElement(descriptor, 1, String.serializer(), value.title)
|
||||
encodeNullableSerializableElement(descriptor, 2, String.serializer(), value.description)
|
||||
encodeBooleanElement(descriptor, 3, value.required)
|
||||
encodeSerializableElement(descriptor, 4, String.serializer(), value.type.toString())
|
||||
encodeNullableSerializableElement(descriptor, 5, serializer(value.type), value.default)
|
||||
encodeNullableSerializableElement(descriptor, 6, MapSerializer(String.serializer(), serializer(value.type)), value.values)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private val patchPrettySerializer by lazy { Json { prettyPrint = true } }
|
||||
private val patchSerializer by lazy { Json }
|
||||
|
||||
/**
|
||||
* Serialize this set of [Patch] to JSON and write it to the given [outputStream].
|
||||
*
|
||||
* @param outputStream The output stream to write the JSON to.
|
||||
* @param prettyPrint Whether to pretty print the JSON.
|
||||
*/
|
||||
@OptIn(ExperimentalSerializationApi::class)
|
||||
fun Set<Patch<*>>.serializeTo(
|
||||
outputStream: OutputStream,
|
||||
prettyPrint: Boolean = true,
|
||||
) = if (prettyPrint) {
|
||||
patchPrettySerializer
|
||||
} else {
|
||||
patchSerializer
|
||||
}.encodeToStream(SetSerializer(PatchSerializer()), this, outputStream)
|
||||
Reference in New Issue
Block a user