perf: 优化校验错误提示

This commit is contained in:
lisonge 2024-01-18 21:12:12 +08:00
parent 01545a77db
commit 25d3c64630
10 changed files with 125 additions and 66 deletions

View File

@ -1,6 +1,5 @@
package li.songe.gkd.data
import kotlinx.parcelize.IgnoredOnParcel
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.JsonArray
import kotlinx.serialization.json.JsonElement
@ -12,6 +11,7 @@ import kotlinx.serialization.json.int
import kotlinx.serialization.json.jsonArray
import kotlinx.serialization.json.jsonObject
import kotlinx.serialization.json.long
import li.songe.gkd.service.allowPropertyNames
import li.songe.gkd.util.json
import li.songe.gkd.util.json5ToJson
import li.songe.selector.Selector
@ -30,7 +30,6 @@ data class RawSubscription(
val apps: List<RawApp> = emptyList(),
) {
@IgnoredOnParcel
val categoryToGroupsMap by lazy {
val allAppGroups = apps.flatMap { a -> a.groups.map { g -> g to a } }
allAppGroups.groupBy { g ->
@ -38,7 +37,6 @@ data class RawSubscription(
}
}
@IgnoredOnParcel
val groupToCategoryMap by lazy {
val map = mutableMapOf<RawAppGroup, RawCategory>()
categoryToGroupsMap.forEach { (key, value) ->
@ -51,12 +49,10 @@ data class RawSubscription(
map
}
@IgnoredOnParcel
val appGroups by lazy {
apps.flatMap { a -> a.groups }
}
@IgnoredOnParcel
val numText by lazy {
val appsSize = apps.size
val appGroupsSize = appGroups.size
@ -166,30 +162,37 @@ data class RawSubscription(
override val rules: List<RawGlobalRule>,
) : RawGroupProps, RawGlobalRuleProps {
@IgnoredOnParcel
val valid by lazy {
rules.all { r ->
r.matches.all { s -> Selector.check(s) } && (r.excludeMatches
?: emptyList()).all { s ->
Selector.check(
s
)
}
}
val allSelectorStrings by lazy {
rules.map { r -> r.matches + (r.excludeMatches ?: emptyList()) }.flatten().distinct()
}
@IgnoredOnParcel
val allExampleUrls by lazy {
mutableListOf<String>().apply {
if (exampleUrls != null) {
addAll(exampleUrls)
}
rules.forEach { r ->
if (r.exampleUrls != null) {
addAll(r.exampleUrls)
}
val allSelector by lazy {
allSelectorStrings.map { s -> Selector.parseOrNull(s) }
}
val unknownPropertyNames by lazy {
allSelector.filterNotNull().flatMap { r -> r.propertyNames.toList() }.distinct()
.filterNot { n -> allowPropertyNames.contains(n) }
}
val errorDesc by lazy {
allSelector.forEachIndexed { i, s ->
if (s == null) {
return@lazy "非法选择器:${allSelectorStrings[i]}"
}
}
unknownPropertyNames.forEach { n ->
return@lazy "非法属性名:${n}"
}
null
}
val valid by lazy { errorDesc == null }
val allExampleUrls by lazy {
((exampleUrls ?: emptyList()) + rules.flatMap { r ->
r.exampleUrls ?: emptyList()
}).distinct()
}
}
@ -239,30 +242,37 @@ data class RawSubscription(
override val rules: List<RawAppRule>,
) : RawGroupProps, RawAppRuleProps {
@IgnoredOnParcel
val valid by lazy {
rules.all { r ->
r.matches.all { s -> Selector.check(s) } && (r.excludeMatches
?: emptyList()).all { s ->
Selector.check(
s
)
}
}
val allSelectorStrings by lazy {
rules.map { r -> r.matches + (r.excludeMatches ?: emptyList()) }.flatten().distinct()
}
@IgnoredOnParcel
val allExampleUrls by lazy {
mutableListOf<String>().apply {
if (exampleUrls != null) {
addAll(exampleUrls)
}
rules.forEach { r ->
if (r.exampleUrls != null) {
addAll(r.exampleUrls)
}
val allSelector by lazy {
allSelectorStrings.map { s -> Selector.parseOrNull(s) }
}
val unknownPropertyNames by lazy {
allSelector.filterNotNull().flatMap { r -> r.propertyNames.toList() }.distinct()
.filterNot { n -> allowPropertyNames.contains(n) }
}
val errorDesc by lazy {
allSelector.forEachIndexed { i, s ->
if (s == null) {
return@lazy "非法选择器:${allSelectorStrings[i]}"
}
}
unknownPropertyNames.forEach { n ->
return@lazy "非法属性名:${n}"
}
null
}
val valid by lazy { errorDesc == null }
val allExampleUrls by lazy {
((exampleUrls ?: emptyList()) + rules.flatMap { r ->
r.exampleUrls ?: emptyList()
}).distinct()
}
}
@ -514,8 +524,7 @@ data class RawSubscription(
},
rules = jsonObject["rules"]?.jsonArray?.map { jsonElement ->
jsonToGlobalRule(jsonElement.jsonObject)
} ?: emptyList()
)
} ?: emptyList())
}
private fun jsonToSubscriptionRaw(rootJson: JsonObject): RawSubscription {
@ -542,8 +551,7 @@ data class RawSubscription(
} ?: emptyList(),
globalGroups = rootJson["globalGroups"]?.jsonArray?.mapIndexed { index, jsonElement ->
jsonToGlobalGroups(jsonElement.jsonObject, index)
} ?: emptyList()
)
} ?: emptyList())
}
private fun <T> List<T>.findDuplicatedItem(predicate: (T) -> Any?): T? {

View File

@ -117,6 +117,36 @@ val getChildren: (AccessibilityNodeInfo) -> Sequence<AccessibilityNodeInfo> = {
}
}
val allowPropertyNames = setOf(
"id",
"vid",
"name",
"text",
"text.length",
"desc",
"desc.length",
"clickable",
"focusable",
"checkable",
"checked",
"editable",
"longClickable",
"visibleToUser",
"left",
"top",
"right",
"bottom",
"width",
"height",
"index",
"depth",
"childCount"
)
private val getAttr: (AccessibilityNodeInfo, String) -> Any? = { node, name ->
when (name) {
"id" -> node.viewIdResourceName

View File

@ -377,8 +377,8 @@ fun AppItemPage(
toast("不能更改规则组的key")
return@TextButton
}
if (!newGroupRaw.valid) {
toast("非法规则:存在非法选择器")
if (newGroupRaw.errorDesc != null) {
toast(newGroupRaw.errorDesc!!)
return@TextButton
}
setEditGroupRaw(null)
@ -499,8 +499,8 @@ fun AppItemPage(
}
newAppRaw.groups
}
if (!tempGroups.all { g -> g.valid }) {
toast("非法规则:存在非法选择器")
tempGroups.find { g -> g.errorDesc != null }?.errorDesc?.let { errorDesc ->
toast(errorDesc)
return@TextButton
}
tempGroups.forEach { g ->

View File

@ -231,8 +231,8 @@ fun GlobalRulePage(subsItemId: Long, focusGroupKey: Int? = null) {
toast("非法规则\n${e.message ?: e}")
return@TextButton
}
if (!newGroup.valid) {
toast("非法规则:存在非法选择器")
if (newGroup.errorDesc != null) {
toast(newGroup.errorDesc!!)
return@TextButton
}
if (rawSubs.globalGroups.any { g -> g.name == newGroup.name }) {
@ -349,8 +349,8 @@ fun GlobalRulePage(subsItemId: Long, focusGroupKey: Int? = null) {
toast("不能更改规则组的key")
return@TextButton
}
if (!newGroupRaw.valid) {
toast("非法规则:存在非法选择器")
if (newGroupRaw.errorDesc != null) {
toast(newGroupRaw.errorDesc!!)
return@TextButton
}
setEditGroupRaw(null)

View File

@ -12,6 +12,7 @@ class CommonSelector private constructor(
val tracks = selector.tracks
val trackIndex = selector.trackIndex
val connectKeys = selector.connectKeys
val propertyNames = selector.propertyNames
fun <T : Any> match(node: T, transform: CommonTransform<T>): T? {
return selector.match(node, transform.transform)
@ -26,7 +27,7 @@ class CommonSelector private constructor(
companion object {
fun parse(source: String) = CommonSelector(Selector.parse(source))
fun check(source: String) = Selector.check(source)
fun parseOrNull(source: String) = Selector.parseOrNull(source)?.let(::CommonSelector)
}
}

View File

@ -34,6 +34,17 @@ class Selector internal constructor(private val propertyWrapper: PropertyWrapper
keys.toTypedArray()
}
val propertyNames by lazy {
var p: PropertyWrapper? = propertyWrapper
val names = mutableSetOf<String>()
while (p != null) {
val s = p!!.propertySegment
p = p!!.to?.to
names.addAll(s.propertyNames)
}
names.distinct().toTypedArray()
}
fun <T> match(
node: T,
transform: Transform<T>,
@ -84,13 +95,10 @@ class Selector internal constructor(private val propertyWrapper: PropertyWrapper
companion object {
fun parse(source: String) = ParserSet.selectorParser(source)
fun check(source: String): Boolean {
return try {
ParserSet.selectorParser(source)
true
} catch (e: Exception) {
false
}
fun parseOrNull(source: String) = try {
ParserSet.selectorParser(source)
} catch (e: Exception) {
null
}
}
}

View File

@ -7,6 +7,8 @@ data class BinaryExpression(val name: String, val operator: CompareOperator, val
override fun <T> match(node: T, transform: Transform<T>) =
operator.compare(transform.getAttr(node, name), value)
override val propertyNames = listOf(name)
override fun toString() = "${name}${operator}${
if (value is String) {
val wrapChar = '"'

View File

@ -4,4 +4,6 @@ import li.songe.selector.Transform
sealed class Expression {
abstract fun <T> match(node: T, transform: Transform<T>): Boolean
abstract val propertyNames: List<String>
}

View File

@ -11,6 +11,8 @@ data class LogicalExpression(
return operator.compare(node, transform, left, right)
}
override val propertyNames = left.propertyNames + right.propertyNames
override fun toString(): String {
val leftStr = if (left is LogicalExpression && left.operator != operator) {
"($left)"

View File

@ -12,12 +12,18 @@ data class PropertySegment(
val name: String,
val expressions: List<Expression>,
) {
private val matchAnyName = name.isBlank() || name == "*"
val propertyNames =
(if (matchAnyName) listOf("name") else emptyList()) + expressions.map { e -> e.propertyNames }
.flatten()
override fun toString(): String {
val matchTag = if (tracked) "@" else ""
return matchTag + name + expressions.joinToString("") { "[$it]" }
}
private val matchName = if (name.isBlank() || name == "*") {
private val matchName = if (matchAnyName) {
object : NodeMatchFc {
override fun <T> invoke(node: T, transform: Transform<T>) = true
}